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
@@ -0,0 +1,485 @@
1
+ """Ultrascale+ PLLs transceiver models."""
2
+
3
+ from typing import List, Union
4
+
5
+ from docplex.cp.modeler import if_then
6
+
7
+ from ...common import core
8
+ from ...converters.converter import converter as conv
9
+ from ...gekko_trans import gekko_translation
10
+ from ...solvers import CpoIntVar, GK_Intermediate, GK_Operators, GKVariable
11
+ from .pll import XilinxPLL
12
+ from .sevenseries import CPLL as SevenSeriesCPLL
13
+ from .sevenseries import QPLL as SevenSeriesQPLL
14
+
15
+
16
+ class UltraScalePlus(XilinxPLL, core, gekko_translation):
17
+ """Ultrascale+ PLLs transceiver models."""
18
+
19
+ # References
20
+ # GTYs
21
+ # https://docs.amd.com/v/u/en-US/ug578-ultrascale-gty-transceivers
22
+ # https://docs.amd.com/r/en-US/ds925-zynq-ultrascale-plus/GTY-Transceiver-Switching-Characteristics
23
+ # GTHs
24
+ # https://docs.amd.com/v/u/en-US/ug576-ultrascale-gth-transceivers
25
+
26
+ transceiver_types_available = ["GTHE4", "GTYE3", "GTYE4"]
27
+ _transceiver_type = "GTHE4"
28
+
29
+ force_cpll = False
30
+ force_qpll = False
31
+ force_qpll1 = False
32
+
33
+ def add_plls(self) -> None:
34
+ """Add PLLs to the model."""
35
+ self.plls = {
36
+ "CPLL": CPLL(self),
37
+ "QPLL": QPLL(self),
38
+ "QPLL1": QPLL1(self),
39
+ }
40
+
41
+ def add_constraints(
42
+ self, config: dict, fpga_ref: Union[int, float], converter: conv
43
+ ) -> dict:
44
+ """Add constraints for PLLs.
45
+
46
+ Args:
47
+ config (dict): Configuration dictionary.
48
+ fpga_ref (int, float): FPGA reference clock.
49
+ converter (conv): Converter object.
50
+
51
+ Returns:
52
+ dict: Updated configuration dictionary.
53
+ """
54
+ assert self.plls, "No PLLs configured. Run the add_plls method"
55
+ assert (
56
+ self.force_cpll + self.force_qpll + self.force_qpll1 <= 1
57
+ ), "Only one PLL can be enabled"
58
+ for pll in self.plls:
59
+ config = self.plls[pll].add_constraints(config, fpga_ref, converter)
60
+ self._add_equation(
61
+ config[converter.name + "_use_cpll"]
62
+ + config[converter.name + "_use_qpll"]
63
+ + config[converter.name + "_use_qpll1"]
64
+ == 1
65
+ )
66
+ return config
67
+
68
+ def get_config(
69
+ self, config: dict, converter: conv, fpga_ref: Union[int, float]
70
+ ) -> dict:
71
+ """Get the configuration of the PLLs.
72
+
73
+ Args:
74
+ config (dict): Configuration dictionary.
75
+ converter (conv): Converter object.
76
+ fpga_ref (int, float): FPGA reference clock.
77
+
78
+ Returns:
79
+ dict: Updated configuration dictionary.
80
+ """
81
+ if self.force_cpll or self.force_qpll or self.force_qpll1:
82
+ if self.force_cpll:
83
+ ecpll = 1
84
+ eqpll = self.solution.get_kpis()[converter.name + "_use_qpll"]
85
+ eqpll1 = self.solution.get_kpis()[converter.name + "_use_qpll1"]
86
+ elif self.force_qpll:
87
+ ecpll = self.solution.get_kpis()[converter.name + "_use_cpll"]
88
+ eqpll = 1
89
+ eqpll1 = self.solution.get_kpis()[converter.name + "_use_qpll1"]
90
+ else:
91
+ ecpll = self.solution.get_kpis()[converter.name + "_use_cpll"]
92
+ eqpll = self.solution.get_kpis()[converter.name + "_use_qpll"]
93
+ eqpll1 = 1
94
+ else:
95
+ ecpll = self.solution.get_kpis()[converter.name + "_use_cpll"]
96
+ eqpll = self.solution.get_kpis()[converter.name + "_use_qpll"]
97
+ eqpll1 = self.solution.get_kpis()[converter.name + "_use_qpll1"]
98
+ assert ecpll + eqpll + eqpll1 == 1, "Only one PLL can be enabled"
99
+ if ecpll:
100
+ pll = "CPLL"
101
+ elif eqpll:
102
+ pll = "QPLL"
103
+ else:
104
+ pll = "QPLL1"
105
+ return self.plls[pll].get_config(config, converter, fpga_ref)
106
+
107
+
108
+ class CPLL(SevenSeriesCPLL):
109
+ """CPLL model for Ultrascale+ transceivers."""
110
+
111
+ @property
112
+ def vco_min(self) -> int:
113
+ """Get the VCO min frequency in Hz for CPLL."""
114
+ if self.parent.transceiver_type in ["GTHE4", "GTYE3", "GTYE4"]:
115
+ return 2000000000
116
+ raise Exception(
117
+ f"Unknown vco_min for transceiver type {self.parent.transceiver_type}"
118
+ )
119
+
120
+ @property
121
+ def vco_max(self) -> int:
122
+ """Get the VCO max frequency in Hz for CPLL."""
123
+ if self.parent.transceiver_type in ["GTHE4", "GTYE3", "GTYE4"]:
124
+ return 6250000000
125
+ raise Exception(
126
+ f"Unknown vco_max for transceiver type {self.parent.transceiver_type}"
127
+ )
128
+
129
+
130
+ class QPLL(SevenSeriesQPLL):
131
+ """QPLL model for Ultrascale+ transceivers."""
132
+
133
+ force_integer_mode = True
134
+
135
+ @property
136
+ def vco_min(self) -> int:
137
+ """Get the VCO min frequency in Hz for QPLL."""
138
+ if self.parent.transceiver_type in ["GTHE3", "GTHE4", "GTYE4"]:
139
+ return 9800000000
140
+ raise Exception(
141
+ f"Unknown vco_min for transceiver type {self.parent.transceiver_type}"
142
+ )
143
+
144
+ @property
145
+ def vco_max(self) -> int:
146
+ """Get the VCO max frequency in Hz for QPLL."""
147
+ if self.parent.transceiver_type in ["GTHE3", "GTHE4", "GTYE4"]:
148
+ return 16375000000
149
+ raise Exception(
150
+ f"Unknown vco_max for transceiver type {self.parent.transceiver_type}"
151
+ )
152
+
153
+ N_available = [*range(16, 160 + 1)]
154
+ _N = [*range(16, 160 + 1)]
155
+
156
+ D_available = [1, 2, 4, 8, 16]
157
+ _D = [1, 2, 4, 8, 16]
158
+ # 32 not available in AC modes https://docs.amd.com/r/en-US/ds925-zynq-ultrascale-plus/GTY-Transceiver-Switching-Characteristics # type: ignore # noqa: B950
159
+
160
+ @property
161
+ def QPLL_CLKOUTRATE_available(self) -> List[int]:
162
+ """Get the QPLL_CLKOUTRATE available values."""
163
+ if self.parent.transceiver_type == "GTHE4":
164
+ return [1, 2]
165
+ return [1]
166
+
167
+ _QPLL_CLKOUTRATE_GTY = [1, 2]
168
+ _QPLL_CLKOUTRATE_GTH = [1, 2]
169
+
170
+ @property
171
+ def QPLL_CLKOUTRATE(self) -> int:
172
+ """Get the QPLL_CLKOUTRATE value."""
173
+ if "GTH" in self.parent.transceiver_type:
174
+ return self._QPLL_CLKOUTRATE_GTH
175
+ return self._QPLL_CLKOUTRATE_GTY
176
+
177
+ @QPLL_CLKOUTRATE.setter
178
+ def QPLL_CLKOUTRATE(self, val: int) -> None:
179
+ """Set the QPLL_CLKOUTRATE.
180
+
181
+ Args:
182
+ val (int): QPLL_CLKOUTRATE value.
183
+
184
+ Raises:
185
+ ValueError: If QPLL_CLKOUTRATE is out of range.
186
+ """
187
+ self._check_in_range(val, self.QPLL_CLKOUTRATE_available, "QPLL_CLKOUTRATE")
188
+ if "GTH" in self.parent.transceiver_type:
189
+ self._QPLL_CLKOUTRATE_GTH = val
190
+ raise ValueError(
191
+ f"QPLL_CLKOUTRATE not available for {self.parent.transceiver_type}"
192
+ )
193
+
194
+ SDMDATA_min_max = [0, 2**24 - 1]
195
+ _SDMDATA_min = 0
196
+
197
+ @property
198
+ def SDMDATA_min(self) -> int:
199
+ """Get the SDMDATA_min value."""
200
+ return self._SDMDATA_min
201
+
202
+ @SDMDATA_min.setter
203
+ def SDMDATA_min(self, val: int) -> None:
204
+ """Set the SDMDATA_min.
205
+
206
+ Args:
207
+ val (int): SDMDATA_min value.
208
+
209
+ Raises:
210
+ ValueError: If SDMDATA_min is out of range.
211
+ """
212
+ if val < self.SDMDATA_min_max[0] or val > self.SDMDATA_min_max[1]:
213
+ raise ValueError(
214
+ f"SDMDATA_min must be between {self.SDMDATA_min_max[0]} and"
215
+ + f" {self.SDMDATA_min_max[1]}"
216
+ )
217
+ self._SDMDATA_min = val
218
+
219
+ _SDMDATA_max = 2**24 - 1
220
+
221
+ @property
222
+ def SDMDATA_max(self) -> int:
223
+ """Get the SDMDATA_max value."""
224
+ return self._SDMDATA_max
225
+
226
+ @SDMDATA_max.setter
227
+ def SDMDATA_max(self, val: int) -> None:
228
+ """Set the SDMDATA_max.
229
+
230
+ Args:
231
+ val (int): SDMDATA_max value.
232
+
233
+ Raises:
234
+ ValueError: If SDMDATA_max is out of range.
235
+ """
236
+ if val < self.SDMDATA_min_max[0] or val > self.SDMDATA_min_max[1]:
237
+ raise ValueError(
238
+ f"SDMDATA must be between {self.SDMDATA_min_max[0]} and"
239
+ + f" {self.SDMDATA_min_max[1]}"
240
+ )
241
+ self._SDMDATA_max = val
242
+
243
+ SDMWIDTH_available = [16, 20, 24]
244
+ _SDMWIDTH = [16, 20, 24]
245
+
246
+ @property
247
+ def SDMWIDTH(self) -> int:
248
+ """Get the SDMWIDTH value."""
249
+ return self._SDMWIDTH
250
+
251
+ @SDMWIDTH.setter
252
+ def SDMWIDTH(self, val: int) -> None:
253
+ """Set the SDMWIDTH.
254
+
255
+ Args:
256
+ val (int): SDMWIDTH value.
257
+ """
258
+ self._check_in_range(val, self.SDMWIDTH_available, "SDMWIDTH")
259
+ self._SDMWIDTH = val
260
+
261
+ _pname = "qpll"
262
+
263
+ def get_config(
264
+ self, config: dict, converter: conv, fpga_ref: Union[int, float]
265
+ ) -> dict:
266
+ """Get the configuration of the QPLL.
267
+
268
+ Args:
269
+ config (dict): Configuration dictionary.
270
+ converter (conv): Converter object.
271
+ fpga_ref (int, float): FPGA reference clock.
272
+
273
+ Returns:
274
+ dict: Updated configuration dictionary.
275
+ """
276
+ pname = self._pname
277
+ pll_config = {"type": self._pname}
278
+ pll_config["n"] = self._get_val(config[converter.name + f"_n_{pname}"])
279
+ pll_config["m"] = self._get_val(config[converter.name + f"_m_{pname}"])
280
+ pll_config["d"] = self._get_val(config[converter.name + f"_d_{pname}"])
281
+ pll_config["clkout_rate"] = self._get_val(
282
+ config[converter.name + f"_clkout_rate_{pname}"]
283
+ )
284
+ if converter.bit_clock < 28.1e9 and not self.force_integer_mode:
285
+ sdm_data = self._get_val(config[converter.name + f"_sdm_data_{pname}"])
286
+ if sdm_data > 0:
287
+ pll_config["sdm_data"] = sdm_data
288
+ pll_config["sdm_width"] = self._get_val(
289
+ config[converter.name + f"_sdm_width_{pname}"]
290
+ )
291
+ pll_config["frac"] = self.solution.get_kpis()[
292
+ converter.name + f"_frac_{pname}"
293
+ ]
294
+ pll_config["n_dot_frac"] = self.solution.get_kpis()[
295
+ converter.name + f"_n_dot_frac_{pname}"
296
+ ]
297
+ else:
298
+ pll_config["n_dot_frac"] = pll_config["n"]
299
+ else:
300
+ pll_config["n_dot_frac"] = pll_config["n"]
301
+
302
+ pll_config["n"] = pll_config["n_dot_frac"]
303
+
304
+ # config['vco'] = self._get_val(config[converter.name + f"_vco_{pname}"])
305
+ pll_config["vco"] = self.solution.get_kpis()[converter.name + f"_vco_{pname}"]
306
+
307
+ # Check
308
+ pll_out = (
309
+ fpga_ref
310
+ * pll_config["n_dot_frac"]
311
+ / (pll_config["m"] * pll_config["clkout_rate"])
312
+ )
313
+ lane_rate = pll_out * 2 / pll_config["d"]
314
+ assert lane_rate == converter.bit_clock, f"{lane_rate} != {converter.bit_clock}"
315
+
316
+ return pll_config
317
+
318
+ def add_constraints(
319
+ self,
320
+ config: dict,
321
+ fpga_ref: Union[int, GKVariable, GK_Intermediate, GK_Operators, CpoIntVar],
322
+ converter: conv,
323
+ ) -> dict:
324
+ """Add constraints for the Transceiver.
325
+
326
+ Args:
327
+ config (dict): Configuration dictionary.
328
+ fpga_ref (int, CpoIntVar): FPGA reference clock.
329
+ converter (conv): Converter object.
330
+
331
+ Returns:
332
+ dict: Updated configuration dictionary.
333
+ """
334
+ pname = self._pname
335
+
336
+ # Global flag to use QPLLn
337
+ if self.parent.force_qpll and self._pname == "qpll":
338
+ v = 1
339
+ elif self.parent.force_qpll1 and self._pname == "qpll1":
340
+ v = 1
341
+ elif self.parent.force_cpll and self._pname == "cpll":
342
+ v = 1
343
+ else:
344
+ v = [0, 1]
345
+
346
+ config[converter.name + f"_use_{pname}"] = self._convert_input(
347
+ v, converter.name + f"_use_{pname}"
348
+ )
349
+ if v == [0, 1]:
350
+ self.model.add_kpi(
351
+ config[converter.name + f"_use_{pname}"],
352
+ name=converter.name + f"_use_{pname}",
353
+ )
354
+
355
+ # Add variables
356
+ config[converter.name + f"_m_{pname}"] = self._convert_input(
357
+ self.M, converter.name + f"_m_{pname}"
358
+ )
359
+ config[converter.name + f"_d_{pname}"] = self._convert_input(
360
+ self.D, converter.name + f"_d_{pname}"
361
+ )
362
+ config[converter.name + f"_n_{pname}"] = self._convert_input(
363
+ self.N, converter.name + f"_n_{pname}"
364
+ )
365
+ config[converter.name + f"_clkout_rate_{pname}"] = self._convert_input(
366
+ self.QPLL_CLKOUTRATE, converter.name + f"_clkout_rate_{pname}"
367
+ )
368
+
369
+ # Frac part
370
+ if converter.bit_clock < 28.1e9 and not self.force_integer_mode:
371
+ config[converter.name + f"_sdm_data_{pname}"] = self.model.integer_var(
372
+ min=self.SDMDATA_min,
373
+ max=self.SDMDATA_max,
374
+ name=converter.name + f"_sdm_data_{pname}",
375
+ )
376
+ config[converter.name + f"_sdm_width_{pname}"] = self._convert_input(
377
+ self.SDMWIDTH, converter.name + f"_sdm_width_{pname}"
378
+ )
379
+ config[converter.name + f"_HIGH_RATE_{pname}"] = self._convert_input(
380
+ self.QPLL_CLKOUTRATE, converter.name + f"_HIGH_RATE_{pname}"
381
+ )
382
+
383
+ # Add intermediate variables
384
+ if converter.bit_clock < 28.1e9 and not self.force_integer_mode:
385
+ config[converter.name + f"_frac_{pname}"] = self._add_intermediate(
386
+ config[converter.name + f"_sdm_data_{pname}"]
387
+ / (2 ** config[converter.name + f"_sdm_width_{pname}"])
388
+ )
389
+ self.model.add_kpi(
390
+ config[converter.name + f"_frac_{pname}"],
391
+ name=converter.name + f"_frac_{pname}",
392
+ )
393
+ self._add_equation([config[converter.name + f"_frac_{pname}"] < 1])
394
+ config[converter.name + f"_n_dot_frac_{pname}"] = self._add_intermediate(
395
+ config[converter.name + f"_n_{pname}"]
396
+ + config[converter.name + f"_frac_{pname}"]
397
+ )
398
+ self.model.add_kpi(
399
+ config[converter.name + f"_n_dot_frac_{pname}"],
400
+ name=converter.name + f"_n_dot_frac_{pname}",
401
+ )
402
+ else:
403
+ config[converter.name + f"_n_dot_frac_{pname}"] = self._add_intermediate(
404
+ config[converter.name + f"_n_{pname}"]
405
+ )
406
+
407
+ config[converter.name + f"_pll_out_{pname}"] = self._add_intermediate(
408
+ fpga_ref
409
+ * config[converter.name + f"_n_dot_frac_{pname}"]
410
+ / (
411
+ config[converter.name + f"_m_{pname}"]
412
+ * config[converter.name + f"_clkout_rate_{pname}"]
413
+ )
414
+ )
415
+ config[converter.name + f"_vco_{pname}"] = self._add_intermediate(
416
+ fpga_ref
417
+ * config[converter.name + f"_n_dot_frac_{pname}"]
418
+ / (
419
+ config[converter.name + f"_m_{pname}"]
420
+ * config[converter.name + f"_clkout_rate_{pname}"]
421
+ )
422
+ )
423
+ self.model.add_kpi(
424
+ config[converter.name + f"_vco_{pname}"],
425
+ name=converter.name + f"_vco_{pname}",
426
+ )
427
+
428
+ # Add constraints
429
+ self._add_equation(
430
+ [
431
+ if_then(
432
+ config[converter.name + f"_use_{pname}"] == 1,
433
+ converter.bit_clock * config[converter.name + f"_d_{pname}"]
434
+ == config[converter.name + f"_pll_out_{pname}"] * 2,
435
+ ),
436
+ # if_then(
437
+ # config[converter.name + f"_use_{pname}"] == 1,
438
+ # config[converter.name + f"_HIGH_RATE_{pname}"]
439
+ # == (converter.bit_clock >= 28.1e9),
440
+ # ),
441
+ ]
442
+ )
443
+
444
+ self._add_equation(
445
+ [
446
+ if_then(
447
+ config[converter.name + f"_use_{pname}"] == 1,
448
+ config[converter.name + f"_vco_{pname}"] >= self.vco_min,
449
+ ),
450
+ if_then(
451
+ config[converter.name + f"_use_{pname}"] == 1,
452
+ config[converter.name + f"_vco_{pname}"] <= self.vco_max,
453
+ ),
454
+ # if_then(
455
+ # config[converter.name + f"_HIGH_RATE_{pname}"] == 1,
456
+ # config[converter.name + f"_frac_{pname}"] == 0,
457
+ # ),
458
+ ]
459
+ )
460
+
461
+ return config
462
+
463
+
464
+ class QPLL1(QPLL):
465
+ """QPLL1 model for Ultrascale+ transceivers."""
466
+
467
+ _pname = "qpll1"
468
+
469
+ @property
470
+ def vco_min(self) -> int:
471
+ """VCO min frequency in Hz for QPLL1."""
472
+ if self.parent.transceiver_type in ["GTHE3", "GTHE4", "GTYE3", "GTYE4"]:
473
+ return 8000000000
474
+ raise Exception(
475
+ f"Unknown vco_min for transceiver type {self.parent.transceiver_type}"
476
+ )
477
+
478
+ @property
479
+ def vco_max(self) -> int:
480
+ """VCO max frequency in Hz for QPLL1."""
481
+ if self.parent.transceiver_type in ["GTHE3", "GTHE4", "GTYE3", "GTYE4"]:
482
+ return 13000000000
483
+ raise Exception(
484
+ f"Unknown vco_max for transceiver type {self.parent.transceiver_type}"
485
+ )