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
|
@@ -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
|
+
)
|