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/clocks/ad9545.py
ADDED
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
"""AD9545 clock chip model."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, List
|
|
4
|
+
|
|
5
|
+
import docplex.cp.catalog as ctg
|
|
6
|
+
import docplex.cp.expression as exp
|
|
7
|
+
import docplex.cp.modeler as mod
|
|
8
|
+
|
|
9
|
+
from adijif.clocks.clock import clock
|
|
10
|
+
from adijif.solvers import CpoSolveResult
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ad9545(clock):
|
|
14
|
+
"""AD9545 clock chip model.
|
|
15
|
+
|
|
16
|
+
Currently this model supports only the internal zero delay mode
|
|
17
|
+
|
|
18
|
+
PLL_in_rate_0 = input_ref_0 / r0
|
|
19
|
+
PLL_in_rate_1 = input_ref_1 / r1
|
|
20
|
+
PLL_in_rate_2 = input_ref_2 / r2
|
|
21
|
+
PLL_in_rate_3 = input_ref_3 / r3
|
|
22
|
+
|
|
23
|
+
PLL_in_rate_0 < PLL_in_max
|
|
24
|
+
PLL_in_rate_1 < PLL_in_max
|
|
25
|
+
PLL_in_rate_2 < PLL_in_max
|
|
26
|
+
PLL_in_rate_3 < PLL_in_max
|
|
27
|
+
|
|
28
|
+
PLL0_rate = n0_profile_0 * PLL_in_rate_0
|
|
29
|
+
PLL0_rate = n0_profile_1 * PLL_in_rate_1
|
|
30
|
+
PLL0_rate = n0_profile_2 * PLL_in_rate_2
|
|
31
|
+
PLL0_rate = n0_profile_3 * PLL_in_rate_3
|
|
32
|
+
|
|
33
|
+
out_rate_0 = PLL0_rate / q0
|
|
34
|
+
out_rate_1 = PLL0_rate / q1
|
|
35
|
+
...
|
|
36
|
+
out_rate_5 = PLL0_rate / q5
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# Limits
|
|
40
|
+
""" Internal limits """
|
|
41
|
+
PLL_out_min = [1200e6, 1600e6]
|
|
42
|
+
PLL_out_max = [1600e6, 2000e6]
|
|
43
|
+
|
|
44
|
+
PLL_in_min = 1
|
|
45
|
+
PLL_in_max = 200000
|
|
46
|
+
|
|
47
|
+
APLL_PFD_MIN = int(162e6)
|
|
48
|
+
APLL_PFD_MAX = int(350e6)
|
|
49
|
+
|
|
50
|
+
""" Output dividers limits
|
|
51
|
+
|
|
52
|
+
Value is programmed on 32 bits but
|
|
53
|
+
2 GHz is the maximum PLL output that feeds
|
|
54
|
+
directly to the output dividers.
|
|
55
|
+
"""
|
|
56
|
+
Q_max = 2000000000
|
|
57
|
+
Q_min = 1
|
|
58
|
+
|
|
59
|
+
""" Input dividers limits
|
|
60
|
+
|
|
61
|
+
Value is programmed on 32 bits but
|
|
62
|
+
750 MHz is the maximum input frequency anyway.
|
|
63
|
+
"""
|
|
64
|
+
R_max = 750000000
|
|
65
|
+
R_min = 1
|
|
66
|
+
|
|
67
|
+
""" PLL N dividers limits
|
|
68
|
+
|
|
69
|
+
Value is programmed on 32 bits.
|
|
70
|
+
|
|
71
|
+
There are constraints on the input and output frequency.
|
|
72
|
+
This places lower and upper bounds on N too.
|
|
73
|
+
|
|
74
|
+
Maximum input frequency is 200 kHz.
|
|
75
|
+
"""
|
|
76
|
+
N_max = 2000000000
|
|
77
|
+
N_min = 1
|
|
78
|
+
|
|
79
|
+
PLL_used = [False, False]
|
|
80
|
+
|
|
81
|
+
avoid_min_max_PLL_rates = True
|
|
82
|
+
minimize_input_dividers = True
|
|
83
|
+
|
|
84
|
+
""" Hitless mode
|
|
85
|
+
|
|
86
|
+
Output phase is alligned with the input phase.
|
|
87
|
+
|
|
88
|
+
If true, DPLL feedback path will no longer be
|
|
89
|
+
the one from APLL output. It will be from a designated
|
|
90
|
+
output divider.
|
|
91
|
+
|
|
92
|
+
These outputs that are used as feedback for DPLL are
|
|
93
|
+
assigned to a specific PLL profile.
|
|
94
|
+
|
|
95
|
+
f_out_fb_source * r_div = dpll_i_N * f_input_ref
|
|
96
|
+
"""
|
|
97
|
+
profiles = {
|
|
98
|
+
"dpll_0_profile_0": {"hitless": False, "fb_source": 0},
|
|
99
|
+
"dpll_0_profile_1": {"hitless": False, "fb_source": 0},
|
|
100
|
+
"dpll_0_profile_2": {"hitless": False, "fb_source": 0},
|
|
101
|
+
"dpll_0_profile_3": {"hitless": False, "fb_source": 0},
|
|
102
|
+
"dpll_1_profile_0": {"hitless": False, "fb_source": 0},
|
|
103
|
+
"dpll_1_profile_1": {"hitless": False, "fb_source": 0},
|
|
104
|
+
"dpll_1_profile_2": {"hitless": False, "fb_source": 0},
|
|
105
|
+
"dpll_1_profile_3": {"hitless": False, "fb_source": 0},
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
config = {
|
|
109
|
+
"r0": 0,
|
|
110
|
+
"r1": 0,
|
|
111
|
+
"r2": 0,
|
|
112
|
+
"r3": 0,
|
|
113
|
+
"PLL0": {},
|
|
114
|
+
"PLL1": {},
|
|
115
|
+
"q0": 0,
|
|
116
|
+
"q1": 0,
|
|
117
|
+
"q2": 0,
|
|
118
|
+
"q3": 0,
|
|
119
|
+
"q4": 0,
|
|
120
|
+
"q5": 0,
|
|
121
|
+
"q6": 0,
|
|
122
|
+
"q7": 0,
|
|
123
|
+
"q8": 0,
|
|
124
|
+
"q9": 0,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
def list_available_references(self) -> List[int]:
|
|
128
|
+
"""List available references for a given divider set."""
|
|
129
|
+
return [self.ref0, self.ref1, self.ref2, self.ref3]
|
|
130
|
+
|
|
131
|
+
def find_dividers(self) -> None:
|
|
132
|
+
"""Find dividers for a given input reference.
|
|
133
|
+
|
|
134
|
+
Not implemented for this model.
|
|
135
|
+
|
|
136
|
+
Raises:
|
|
137
|
+
NotImplementedError: Always
|
|
138
|
+
"""
|
|
139
|
+
raise NotImplementedError
|
|
140
|
+
|
|
141
|
+
def get_config(self, solution: CpoSolveResult = None) -> Dict:
|
|
142
|
+
"""Extract configurations from solver results.
|
|
143
|
+
|
|
144
|
+
Collect internal clock chip configuration and output clock definitions
|
|
145
|
+
leading to connected devices (converters, FPGAs)
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
solution (CpoSolveResult): CPlex solution. Only needed for CPlex solver
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
Dict: Dictionary of clocking rates and dividers for configuration
|
|
152
|
+
"""
|
|
153
|
+
if solution:
|
|
154
|
+
self.solution = solution
|
|
155
|
+
|
|
156
|
+
config: Dict = {
|
|
157
|
+
"PLL0": {},
|
|
158
|
+
"PLL1": {},
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
for i in range(0, 4):
|
|
162
|
+
config["r" + str(i)] = self._get_val(self.config["r" + str(i)])
|
|
163
|
+
|
|
164
|
+
for i in range(0, 2):
|
|
165
|
+
for j in range(0, 4):
|
|
166
|
+
if self.input_refs[j] != 0 and self.PLL_used[i]:
|
|
167
|
+
n_name = "n" + str(i) + "_profile_" + str(j)
|
|
168
|
+
n_dpll_name = "n_dpll" + str(i) + "_profile_" + str(j)
|
|
169
|
+
|
|
170
|
+
config["PLL" + str(i)][n_dpll_name] = self._get_val(
|
|
171
|
+
self.config[n_dpll_name]
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
config["PLL" + str(i)][n_name] = self._get_val(self.config[n_name])
|
|
175
|
+
|
|
176
|
+
config["PLL" + str(i)]["rate_hz"] = self._get_val(
|
|
177
|
+
self.config["PLL" + str(i) + "_rate"]
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
for i in range(0, 2):
|
|
181
|
+
for j in range(0, 4):
|
|
182
|
+
dpll_profile_name = "dpll_" + str(i) + "_profile_" + str(j)
|
|
183
|
+
|
|
184
|
+
if self.PLL_used[i] and dpll_profile_name in self.profiles:
|
|
185
|
+
if self.profiles[dpll_profile_name]["hitless"]:
|
|
186
|
+
source_nr = int(self.profiles[dpll_profile_name]["fb_source"])
|
|
187
|
+
|
|
188
|
+
config["PLL" + str(i)]["hitless"] = {
|
|
189
|
+
"fb_source": source_nr,
|
|
190
|
+
"fb_source_rate": int(self.out_freqs[source_nr]),
|
|
191
|
+
}
|
|
192
|
+
break
|
|
193
|
+
|
|
194
|
+
for i in range(0, 10):
|
|
195
|
+
config["q" + str(i)] = self._get_val(self.config["q" + str(i)])
|
|
196
|
+
|
|
197
|
+
return config
|
|
198
|
+
|
|
199
|
+
def _setup_solver_constraints(
|
|
200
|
+
self, input_refs: List[int], out_freqs: List[int]
|
|
201
|
+
) -> None:
|
|
202
|
+
"""Apply constraints to solver model.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
input_refs (List[int]): 4 references (frequency in hertz)
|
|
206
|
+
out_freqs (List[int]): 10 outputs (frequency in hertz)
|
|
207
|
+
|
|
208
|
+
Raises:
|
|
209
|
+
Exception: Invalid solver
|
|
210
|
+
"""
|
|
211
|
+
self.input_refs = input_refs
|
|
212
|
+
|
|
213
|
+
self.PLL_used = [False, False]
|
|
214
|
+
for i in range(0, 10):
|
|
215
|
+
if out_freqs[i] != 0:
|
|
216
|
+
if i > 5:
|
|
217
|
+
self.PLL_used[1] = True
|
|
218
|
+
else:
|
|
219
|
+
self.PLL_used[0] = True
|
|
220
|
+
|
|
221
|
+
equations = []
|
|
222
|
+
|
|
223
|
+
if self.solver == "gekko":
|
|
224
|
+
"""Add divider as variables to the model"""
|
|
225
|
+
for i in range(0, 4):
|
|
226
|
+
if input_refs[i] != 0:
|
|
227
|
+
"""If the user does not set a specific r,
|
|
228
|
+
turn it into a variable"""
|
|
229
|
+
r_div_int = isinstance(self.config["r" + str(i)], int)
|
|
230
|
+
if r_div_int and self.config["r" + str(i)] != 0:
|
|
231
|
+
self.config["r" + str(i)] = self.model.Const(
|
|
232
|
+
int(self.config["r" + str(i)]),
|
|
233
|
+
name=("r" + str(i)),
|
|
234
|
+
)
|
|
235
|
+
else:
|
|
236
|
+
self.config["r" + str(i)] = self.model.Var(
|
|
237
|
+
integer=True,
|
|
238
|
+
lb=self.R_min,
|
|
239
|
+
ub=self.R_max,
|
|
240
|
+
name=("r" + str(i)),
|
|
241
|
+
)
|
|
242
|
+
self.config["input_ref_" + str(i)] = self.model.Const(
|
|
243
|
+
int(input_refs[i]),
|
|
244
|
+
name=("input_ref_" + str(i)),
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
""" Add PLL N as variables to the model for each PLL profile """
|
|
248
|
+
for i in range(0, 2):
|
|
249
|
+
if self.PLL_used[i]:
|
|
250
|
+
"""Force PLL input rates and output rates
|
|
251
|
+
to be integer values"""
|
|
252
|
+
self.config["PLL" + str(i) + "_rate"] = self.model.Var(
|
|
253
|
+
integer=True,
|
|
254
|
+
lb=self.PLL_out_min[i],
|
|
255
|
+
ub=self.PLL_out_max[i],
|
|
256
|
+
name=("PLL" + str(i) + "_rate"),
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
for j in range(0, 4):
|
|
260
|
+
if input_refs[j] != 0:
|
|
261
|
+
n_name = "n" + str(i) + "_profile_" + str(j)
|
|
262
|
+
n_dpll_name = "n_dpll" + str(i) + "_profile_" + str(j)
|
|
263
|
+
m_apll_name = "m_apll" + str(i) + "_profile_" + str(j)
|
|
264
|
+
|
|
265
|
+
self.config[n_name] = self.model.Var(
|
|
266
|
+
integer=True,
|
|
267
|
+
lb=self.N_min,
|
|
268
|
+
ub=self.N_max,
|
|
269
|
+
name=n_name,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
""" Internally the PLL block is composed of a
|
|
273
|
+
Digital PLL and an Analog PLL with different
|
|
274
|
+
constraints on dividers.
|
|
275
|
+
"""
|
|
276
|
+
DPLL_N = self.model.Var(
|
|
277
|
+
integer=True, lb=1, ub=350e6, name=n_dpll_name
|
|
278
|
+
)
|
|
279
|
+
self.config[n_dpll_name] = DPLL_N
|
|
280
|
+
|
|
281
|
+
APLL_M = self.model.Var(
|
|
282
|
+
integer=True, lb=7, ub=255, name=m_apll_name
|
|
283
|
+
)
|
|
284
|
+
self.config[m_apll_name] = APLL_M
|
|
285
|
+
|
|
286
|
+
equations = equations + [
|
|
287
|
+
self.config[n_name] == DPLL_N * APLL_M
|
|
288
|
+
]
|
|
289
|
+
|
|
290
|
+
elif self.solver == "CPLEX":
|
|
291
|
+
"""Add divider as variables to the model"""
|
|
292
|
+
for i in range(0, 4):
|
|
293
|
+
if input_refs[i] != 0:
|
|
294
|
+
"""If the user does not set a specific r,
|
|
295
|
+
turn it into a variable
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
r_div_int = isinstance(self.config["r" + str(i)], int)
|
|
299
|
+
if r_div_int and self.config["r" + str(i)] != 0:
|
|
300
|
+
self.config["input_ref_" + str(i)] = exp.CpoValue(
|
|
301
|
+
int(self.config["r" + str(i)]), type=ctg.Type_Int
|
|
302
|
+
)
|
|
303
|
+
else:
|
|
304
|
+
self.config["r" + str(i)] = exp.integer_var(
|
|
305
|
+
int(self.R_min), int(self.R_max), "r" + str(i)
|
|
306
|
+
)
|
|
307
|
+
self.config["input_ref_" + str(i)] = exp.CpoValue(
|
|
308
|
+
int(input_refs[i]), type=ctg.Type_Int
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
""" Add PLL N as variables to the model for each PLL profile """
|
|
312
|
+
for i in range(0, 2):
|
|
313
|
+
if self.PLL_used[i]:
|
|
314
|
+
self.config["PLL" + str(i) + "_rate"] = exp.integer_var(
|
|
315
|
+
int(self.PLL_out_min[i]),
|
|
316
|
+
int(self.PLL_out_max[i]),
|
|
317
|
+
"PLL" + str(i) + "_rate",
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
for j in range(0, 4):
|
|
321
|
+
if input_refs[j] != 0:
|
|
322
|
+
n_name = "n" + str(i) + "_profile_" + str(j)
|
|
323
|
+
n_dpll_name = "n_dpll" + str(i) + "_profile_" + str(j)
|
|
324
|
+
m_apll_name = "m_apll" + str(i) + "_profile_" + str(j)
|
|
325
|
+
|
|
326
|
+
self.config[n_name] = exp.integer_var(
|
|
327
|
+
int(self.N_min), int(self.N_max), n_name
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
""" Internally the PLL block is composed of a Digital
|
|
331
|
+
PLL and an Analog PLL with different constraints on
|
|
332
|
+
dividers
|
|
333
|
+
"""
|
|
334
|
+
DPLL_N = exp.integer_var(int(1), int(350e6), n_dpll_name)
|
|
335
|
+
self.config[n_dpll_name] = DPLL_N
|
|
336
|
+
APLL_M = exp.integer_var(int(7), int(255), m_apll_name)
|
|
337
|
+
self.config[m_apll_name] = APLL_M
|
|
338
|
+
|
|
339
|
+
equations = equations + [
|
|
340
|
+
self.config[n_name] == DPLL_N * APLL_M
|
|
341
|
+
]
|
|
342
|
+
else:
|
|
343
|
+
raise Exception("Unknown solver {}".format(self.solver))
|
|
344
|
+
|
|
345
|
+
for i in range(0, 4):
|
|
346
|
+
if self.input_refs[i] != 0:
|
|
347
|
+
if self.solver == "gekko":
|
|
348
|
+
self.config["PLL_in_rate_" + str(i)] = self.model.Var(
|
|
349
|
+
integer=True,
|
|
350
|
+
lb=self.PLL_in_min,
|
|
351
|
+
ub=self.PLL_in_max,
|
|
352
|
+
name=("PLL_in_rate_" + str(i)),
|
|
353
|
+
)
|
|
354
|
+
elif self.solver == "CPLEX":
|
|
355
|
+
self.config["PLL_in_rate_" + str(i)] = exp.integer_var(
|
|
356
|
+
int(self.PLL_in_min),
|
|
357
|
+
int(self.PLL_in_max),
|
|
358
|
+
"PLL_in_rate_" + str(i),
|
|
359
|
+
)
|
|
360
|
+
else:
|
|
361
|
+
raise Exception("Unknown solver {}".format(self.solver))
|
|
362
|
+
|
|
363
|
+
equations = equations + [
|
|
364
|
+
self.config["PLL_in_rate_" + str(i)] * self.config["r" + str(i)]
|
|
365
|
+
== self.config["input_ref_" + str(i)]
|
|
366
|
+
]
|
|
367
|
+
|
|
368
|
+
for i in range(0, 2):
|
|
369
|
+
for j in range(0, 4):
|
|
370
|
+
if self.input_refs[j] != 0 and self.PLL_used[i]:
|
|
371
|
+
n_name = "n" + str(i) + "_profile_" + str(j)
|
|
372
|
+
n_dpll_name = "n_dpll" + str(i) + "_profile_" + str(j)
|
|
373
|
+
m_apll_name = "m_apll" + str(i) + "_profile_" + str(j)
|
|
374
|
+
dpll_profile_name = "dpll_" + str(i) + "_profile_" + str(j)
|
|
375
|
+
|
|
376
|
+
pll_rate = self.config["PLL" + str(i) + "_rate"]
|
|
377
|
+
pll_in_rate = self.config["PLL_in_rate_" + str(j)]
|
|
378
|
+
pll_m_div = self.config[m_apll_name]
|
|
379
|
+
pll_n_div = self.config[n_dpll_name]
|
|
380
|
+
|
|
381
|
+
if not self.profiles[dpll_profile_name]["hitless"]:
|
|
382
|
+
equations = equations + [
|
|
383
|
+
self.config[n_name] * pll_in_rate == pll_rate
|
|
384
|
+
]
|
|
385
|
+
|
|
386
|
+
""" Limit APLL PFD input values """
|
|
387
|
+
equations = equations + [
|
|
388
|
+
(pll_n_div * pll_in_rate) >= self.APLL_PFD_MIN,
|
|
389
|
+
(pll_n_div * pll_in_rate) <= self.APLL_PFD_MAX,
|
|
390
|
+
]
|
|
391
|
+
else:
|
|
392
|
+
"""Limit APLL PFD input values"""
|
|
393
|
+
equations = equations + [
|
|
394
|
+
pll_rate >= self.APLL_PFD_MIN * pll_m_div,
|
|
395
|
+
pll_rate <= self.APLL_PFD_MAX * pll_m_div,
|
|
396
|
+
]
|
|
397
|
+
|
|
398
|
+
self._add_equation(equations)
|
|
399
|
+
|
|
400
|
+
cplex_objectives = []
|
|
401
|
+
|
|
402
|
+
""" Instruct solver to try to avoid Minimum and Maximum PLL rates """
|
|
403
|
+
if self.avoid_min_max_PLL_rates:
|
|
404
|
+
for i in range(0, 2):
|
|
405
|
+
if self.PLL_used[i]:
|
|
406
|
+
average_PLL_rate = self.PLL_out_min[i] / 2 + self.PLL_out_max[i] / 2
|
|
407
|
+
|
|
408
|
+
if self.solver == "CPLEX":
|
|
409
|
+
cplex_objectives = cplex_objectives + [
|
|
410
|
+
mod.abs(
|
|
411
|
+
self.config["PLL" + str(i) + "_rate"] - average_PLL_rate
|
|
412
|
+
)
|
|
413
|
+
]
|
|
414
|
+
elif self.solver == "gekko":
|
|
415
|
+
self.model.Minimize(
|
|
416
|
+
self.model.abs3(
|
|
417
|
+
self.config["PLL" + str(i) + "_rate"] - average_PLL_rate
|
|
418
|
+
)
|
|
419
|
+
)
|
|
420
|
+
else:
|
|
421
|
+
raise Exception("Unknown solver {}".format(self.solver))
|
|
422
|
+
|
|
423
|
+
""" Instruct solver to maximize PLL input rates """
|
|
424
|
+
if self.minimize_input_dividers:
|
|
425
|
+
for i in range(0, 4):
|
|
426
|
+
if self.input_refs[i] != 0:
|
|
427
|
+
if self.solver == "CPLEX":
|
|
428
|
+
cplex_objectives = cplex_objectives + [
|
|
429
|
+
self.config["r" + str(i)]
|
|
430
|
+
]
|
|
431
|
+
elif self.solver == "gekko":
|
|
432
|
+
self.model.Maximize(self.config["PLL_in_rate_" + str(i)])
|
|
433
|
+
else:
|
|
434
|
+
raise Exception("Unknown solver {}".format(self.solver))
|
|
435
|
+
|
|
436
|
+
if self.solver == "CPLEX" and len(cplex_objectives) != 0:
|
|
437
|
+
self.model.add(mod.minimize_static_lex(cplex_objectives))
|
|
438
|
+
|
|
439
|
+
def _setup(self, input_refs: List[int], out_freqs: List[int]) -> None:
|
|
440
|
+
# Setup clock chip internal constraints
|
|
441
|
+
self.out_freqs = out_freqs
|
|
442
|
+
|
|
443
|
+
self._setup_solver_constraints(input_refs, out_freqs)
|
|
444
|
+
|
|
445
|
+
def set_requested_clocks(self, ins: List[int], outs: List[int]) -> None:
|
|
446
|
+
"""Define necessary clocks to be generated in model.
|
|
447
|
+
|
|
448
|
+
Args:
|
|
449
|
+
ins (List[int]): list of input references rates
|
|
450
|
+
outs (List[int]): list of output rates required
|
|
451
|
+
|
|
452
|
+
Raises:
|
|
453
|
+
Exception: if the number of input references is not equal to the
|
|
454
|
+
number of output rates
|
|
455
|
+
"""
|
|
456
|
+
input_refs = [0] * 4
|
|
457
|
+
out_freqs = [0] * 10
|
|
458
|
+
|
|
459
|
+
for in_ref in ins:
|
|
460
|
+
input_refs[in_ref[0]] = in_ref[1]
|
|
461
|
+
|
|
462
|
+
for out_ref in outs:
|
|
463
|
+
out_freqs[out_ref[0]] = out_ref[1]
|
|
464
|
+
|
|
465
|
+
self._setup(input_refs, out_freqs)
|
|
466
|
+
|
|
467
|
+
""" Add output dividers as variables to the model """
|
|
468
|
+
for i in range(0, 10):
|
|
469
|
+
if out_freqs[i] != 0:
|
|
470
|
+
if self.solver == "gekko":
|
|
471
|
+
self.config["q" + str(i)] = self.model.Var(
|
|
472
|
+
integer=True,
|
|
473
|
+
lb=self.Q_min,
|
|
474
|
+
ub=self.Q_max,
|
|
475
|
+
name=("q" + str(i)),
|
|
476
|
+
)
|
|
477
|
+
self.config["out_rate_" + str(i)] = self.model.Const(
|
|
478
|
+
int(out_freqs[i]), name=("out_rate_" + str(i))
|
|
479
|
+
)
|
|
480
|
+
elif self.solver == "CPLEX":
|
|
481
|
+
self.config["q" + str(i)] = exp.integer_var(
|
|
482
|
+
int(self.Q_min), int(self.Q_max), "q" + str(i)
|
|
483
|
+
)
|
|
484
|
+
self.config["out_rate_" + str(i)] = exp.CpoValue(
|
|
485
|
+
value=int(out_freqs[i]), type=ctg.Type_Int
|
|
486
|
+
)
|
|
487
|
+
else:
|
|
488
|
+
raise Exception("Unknown solver {}".format(self.solver))
|
|
489
|
+
|
|
490
|
+
""" Add hitless mode constraints for profiles that activate it """
|
|
491
|
+
for i in range(0, 2):
|
|
492
|
+
for j in range(0, 4):
|
|
493
|
+
dpll_profile_name = "dpll_" + str(i) + "_profile_" + str(j)
|
|
494
|
+
|
|
495
|
+
if self.PLL_used[i] and self.profiles[dpll_profile_name]["hitless"]:
|
|
496
|
+
source_nr = int(self.profiles[dpll_profile_name]["fb_source"])
|
|
497
|
+
n_dpll_name = "n_dpll" + str(i) + "_profile_" + str(j)
|
|
498
|
+
|
|
499
|
+
if out_freqs[source_nr] == 0:
|
|
500
|
+
raise Exception(
|
|
501
|
+
"Hitless profile: {}, has an invalid source.".format(
|
|
502
|
+
dpll_profile_name
|
|
503
|
+
)
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
input_ref = self.config["input_ref_" + str(j)]
|
|
507
|
+
pll_n_div = self.config[n_dpll_name]
|
|
508
|
+
out_rate = self.config["out_rate_" + str(source_nr)]
|
|
509
|
+
r_div = self.config["r" + str(j)]
|
|
510
|
+
|
|
511
|
+
""" Frequency translation factor:
|
|
512
|
+
N * input_ref_j == out_rate_x * r_div_j
|
|
513
|
+
"""
|
|
514
|
+
self._add_equation([input_ref * pll_n_div == out_rate * r_div])
|
|
515
|
+
|
|
516
|
+
""" Hitless mode places a strict constraint on Q dividers """
|
|
517
|
+
self.config["q" + str(i)]
|
|
518
|
+
self._add_equation([self.config["q" + str(source_nr)] >= 8])
|
|
519
|
+
|
|
520
|
+
for i in range(0, 10):
|
|
521
|
+
if out_freqs[i] != 0:
|
|
522
|
+
if i > 5:
|
|
523
|
+
pll_number = 1
|
|
524
|
+
else:
|
|
525
|
+
pll_number = 0
|
|
526
|
+
|
|
527
|
+
self._add_equation(
|
|
528
|
+
[
|
|
529
|
+
self.config["PLL" + str(pll_number) + "_rate"]
|
|
530
|
+
== self.config["out_rate_" + str(i)] * self.config["q" + str(i)]
|
|
531
|
+
]
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
def _solve_gekko(self) -> bool:
|
|
535
|
+
"""Local solve method for clock model.
|
|
536
|
+
|
|
537
|
+
Call model solver with correct arguments.
|
|
538
|
+
|
|
539
|
+
Returns:
|
|
540
|
+
bool: Always False
|
|
541
|
+
"""
|
|
542
|
+
self.model.options.SOLVER = 1 # APOPT solver
|
|
543
|
+
self.model.solver_options = [
|
|
544
|
+
"minlp_maximum_iterations 300000000",
|
|
545
|
+
"minlp_max_iter_with_int_sol 6000",
|
|
546
|
+
"minlp_as_nlp 0",
|
|
547
|
+
"minlp_branch_method 1",
|
|
548
|
+
"minlp_integer_tol 0.0001",
|
|
549
|
+
]
|
|
550
|
+
|
|
551
|
+
self.model.solve(disp=False)
|
|
552
|
+
|
|
553
|
+
return False
|