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/ad9528.py
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
"""AD9528 clock chip model."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, List, Union
|
|
4
|
+
|
|
5
|
+
from adijif.clocks.ad9528_bf import ad9528_bf
|
|
6
|
+
from adijif.solvers import CpoExpr, CpoSolveResult, GK_Intermediate
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ad9528(ad9528_bf):
|
|
10
|
+
"""AD9528 clock chip model.
|
|
11
|
+
|
|
12
|
+
This model currently supports VCXO+PLL2 configurations
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
name = "AD9528"
|
|
16
|
+
|
|
17
|
+
# Ranges
|
|
18
|
+
""" VCO divider """
|
|
19
|
+
m1_available = [3, 4, 5]
|
|
20
|
+
""" Output dividers """
|
|
21
|
+
d_available = [*range(1, 1024)]
|
|
22
|
+
""" sysref dividers """
|
|
23
|
+
k_available = [*range(0, 65536)]
|
|
24
|
+
""" VCXO multiplier """
|
|
25
|
+
n2_available = [*range(1, 256)]
|
|
26
|
+
""" VCO calibration dividers """
|
|
27
|
+
a_available = [0, 1, 2, 3]
|
|
28
|
+
b_availble = [*range(3, 64)]
|
|
29
|
+
# N = (PxB) + A, P=4, A==[0,1,2,3], B=[3..63]
|
|
30
|
+
# See table 46 of DS for limits
|
|
31
|
+
""" VCXO dividers """
|
|
32
|
+
r1_available = [*range(1, 32)]
|
|
33
|
+
|
|
34
|
+
# State management
|
|
35
|
+
_clk_names: List[str] = []
|
|
36
|
+
|
|
37
|
+
# Defaults
|
|
38
|
+
_m1: Union[List[int], int] = [3, 4, 5]
|
|
39
|
+
_d: Union[List[int], int] = [*range(1, 1024)]
|
|
40
|
+
_k: Union[List[int], int] = k_available
|
|
41
|
+
_n2: Union[List[int], int] = n2_available
|
|
42
|
+
_r1: Union[List[int], int] = [*range(1, 32)]
|
|
43
|
+
_a: Union[List[int], int] = [*range(0, 4)]
|
|
44
|
+
_b: Union[List[int], int] = [*range(3, 64)]
|
|
45
|
+
|
|
46
|
+
# Limits
|
|
47
|
+
vco_min = 3450e6
|
|
48
|
+
vco_max = 4025e6
|
|
49
|
+
pfd_max = 275e6
|
|
50
|
+
|
|
51
|
+
minimize_feedback_dividers = True
|
|
52
|
+
|
|
53
|
+
use_vcxo_double = False
|
|
54
|
+
vcxo = 125e6
|
|
55
|
+
|
|
56
|
+
# sysref parameters
|
|
57
|
+
sysref_external = False
|
|
58
|
+
_sysref = None
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def m1(self) -> Union[int, List[int]]:
|
|
62
|
+
"""VCO divider path 1.
|
|
63
|
+
|
|
64
|
+
Valid dividers are 3,4,5
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
int: Current allowable dividers
|
|
68
|
+
"""
|
|
69
|
+
return self._m1
|
|
70
|
+
|
|
71
|
+
@m1.setter
|
|
72
|
+
def m1(self, value: Union[int, List[int]]) -> None:
|
|
73
|
+
"""VCO divider path 1.
|
|
74
|
+
|
|
75
|
+
Valid dividers are 3,4,5
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
value (int, list[int]): Allowable values for divider
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
self._check_in_range(value, self.m1_available, "m1")
|
|
82
|
+
self._m1 = value
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def d(self) -> Union[int, List[int]]:
|
|
86
|
+
"""Output dividers.
|
|
87
|
+
|
|
88
|
+
Valid dividers are 1->1023
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
int: Current allowable dividers
|
|
92
|
+
"""
|
|
93
|
+
return self._d
|
|
94
|
+
|
|
95
|
+
@d.setter
|
|
96
|
+
def d(self, value: Union[int, List[int]]) -> None:
|
|
97
|
+
"""Output dividers.
|
|
98
|
+
|
|
99
|
+
Valid dividers are 1->1023
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
value (int, list[int]): Allowable values for divider
|
|
103
|
+
|
|
104
|
+
"""
|
|
105
|
+
self._check_in_range(value, self.d_available, "d")
|
|
106
|
+
self._d = value
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def k(self) -> Union[int, List[int]]:
|
|
110
|
+
"""Sysref dividers.
|
|
111
|
+
|
|
112
|
+
Valid dividers are 0->65535
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
int: Current allowable dividers
|
|
116
|
+
"""
|
|
117
|
+
return self._k
|
|
118
|
+
|
|
119
|
+
@k.setter
|
|
120
|
+
def k(self, value: Union[int, List[int]]) -> None:
|
|
121
|
+
"""Sysref dividers.
|
|
122
|
+
|
|
123
|
+
Valid dividers are 0->65535
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
value (int, list[int]): Allowable values for divider
|
|
127
|
+
|
|
128
|
+
"""
|
|
129
|
+
self._check_in_range(value, self.d_available, "k")
|
|
130
|
+
self._k = value
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def n2(self) -> Union[int, List[int]]:
|
|
134
|
+
"""n2: VCO feedback divider.
|
|
135
|
+
|
|
136
|
+
Valid dividers are 1->255
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
int: Current allowable dividers
|
|
140
|
+
"""
|
|
141
|
+
return self._n2
|
|
142
|
+
|
|
143
|
+
@n2.setter
|
|
144
|
+
def n2(self, value: Union[int, List[int]]) -> None:
|
|
145
|
+
"""VCO feedback divider.
|
|
146
|
+
|
|
147
|
+
Valid dividers are 1->255
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
value (int, list[int]): Allowable values for divider
|
|
151
|
+
|
|
152
|
+
"""
|
|
153
|
+
self._check_in_range(value, self.n2_available, "n2")
|
|
154
|
+
self._n2 = value
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def r1(self) -> Union[int, List[int]]:
|
|
158
|
+
"""VCXO input dividers.
|
|
159
|
+
|
|
160
|
+
Valid dividers are 1->31
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
int: Current allowable dividers
|
|
164
|
+
"""
|
|
165
|
+
return self._r1
|
|
166
|
+
|
|
167
|
+
@r1.setter
|
|
168
|
+
def r1(self, value: Union[int, List[int]]) -> None:
|
|
169
|
+
"""VCXO input dividers.
|
|
170
|
+
|
|
171
|
+
Valid dividers are 1->31
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
value (int, list[int]): Allowable values for divider
|
|
175
|
+
|
|
176
|
+
"""
|
|
177
|
+
self._check_in_range(value, self.r1_available, "r1")
|
|
178
|
+
self._r1 = value
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def a(self) -> Union[int, List[int]]:
|
|
182
|
+
"""VCO calibration divider 1.
|
|
183
|
+
|
|
184
|
+
Valid dividers are 0->3
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
int: Current allowable dividers
|
|
188
|
+
"""
|
|
189
|
+
return self._a
|
|
190
|
+
|
|
191
|
+
@a.setter
|
|
192
|
+
def a(self, value: Union[int, List[int]]) -> None:
|
|
193
|
+
"""VCO calibration divider 1.
|
|
194
|
+
|
|
195
|
+
Valid dividers are 0->3
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
value (int, list[int]): Allowable values for counter
|
|
199
|
+
|
|
200
|
+
"""
|
|
201
|
+
self._check_in_range(value, self.a_available, "a")
|
|
202
|
+
self._a = value
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def b(self) -> Union[int, List[int]]:
|
|
206
|
+
"""VCO calibration divider 2.
|
|
207
|
+
|
|
208
|
+
Valid dividers are 3->63
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
int: Current allowable dividers
|
|
212
|
+
"""
|
|
213
|
+
return self._b
|
|
214
|
+
|
|
215
|
+
@b.setter
|
|
216
|
+
def b(self, value: Union[int, List[int]]) -> None:
|
|
217
|
+
"""VCO calibration divider 2.
|
|
218
|
+
|
|
219
|
+
Valid dividers are 3->63
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
value (int, list[int]): Allowable values for counter
|
|
223
|
+
|
|
224
|
+
"""
|
|
225
|
+
self._check_in_range(value, self.b_available, "b")
|
|
226
|
+
self._b = value
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def vco(self) -> float:
|
|
230
|
+
"""VCO Frequency in Hz.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
float: computed VCO frequency
|
|
234
|
+
"""
|
|
235
|
+
r1 = self._get_val(self.config["r1"])
|
|
236
|
+
m1 = self._get_val(self.config["m1"])
|
|
237
|
+
n2 = self._get_val(self.config["n2"])
|
|
238
|
+
|
|
239
|
+
return self.vcxo / r1 * m1 * n2
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def sysref(self) -> int:
|
|
243
|
+
"""SYSREF Frequency in Hz.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
int: computed sysref frequency
|
|
247
|
+
"""
|
|
248
|
+
r1 = self._get_val(self.config["r1"])
|
|
249
|
+
k = self._get_val(self.config["k"])
|
|
250
|
+
|
|
251
|
+
if self.sysref_external:
|
|
252
|
+
sysref_src = self.vcxo
|
|
253
|
+
else:
|
|
254
|
+
sysref_src = self.vcxo / r1
|
|
255
|
+
|
|
256
|
+
return sysref_src / (2 * k)
|
|
257
|
+
|
|
258
|
+
@sysref.setter
|
|
259
|
+
def sysref(self, value: Union[int, float]) -> None:
|
|
260
|
+
"""Set sysref frequency.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
value (int, float): Frequency
|
|
264
|
+
"""
|
|
265
|
+
self._sysref = int(value)
|
|
266
|
+
|
|
267
|
+
def get_config(self, solution: CpoSolveResult = None) -> Dict:
|
|
268
|
+
"""Extract configurations from solver results.
|
|
269
|
+
|
|
270
|
+
Collect internal clock chip configuration and output clock definitions
|
|
271
|
+
leading to connected devices (converters, FPGAs)
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
solution (CpoSolveResult): CPlex solution. Only needed for CPlex solver
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
Dict: Dictionary of clocking rates and dividers for configuration
|
|
278
|
+
|
|
279
|
+
Raises:
|
|
280
|
+
Exception: If solver is not called first
|
|
281
|
+
"""
|
|
282
|
+
if not self._clk_names:
|
|
283
|
+
raise Exception("set_requested_clocks must be called before get_config")
|
|
284
|
+
|
|
285
|
+
if solution:
|
|
286
|
+
self.solution = solution
|
|
287
|
+
|
|
288
|
+
# out_dividers = [solution.get_value(x) for x in self.config["out_dividers"]]
|
|
289
|
+
out_dividers = [self._get_val(x) for x in self.config["out_dividers"]]
|
|
290
|
+
|
|
291
|
+
config: Dict = {
|
|
292
|
+
"vcxo": self.vcxo / 2 if self.use_vcxo_double else self.vcxo,
|
|
293
|
+
"vco": self.vco,
|
|
294
|
+
"r1": self._get_val(self.config["r1"]),
|
|
295
|
+
"n2": self._get_val(self.config["n2"]),
|
|
296
|
+
"m1": self._get_val(self.config["m1"]),
|
|
297
|
+
"a": self._get_val(self.config["a"]),
|
|
298
|
+
"b": self._get_val(self.config["b"]),
|
|
299
|
+
"out_dividers": out_dividers,
|
|
300
|
+
"output_clocks": [],
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if self._sysref:
|
|
304
|
+
config["k"] = self._get_val(self.config["k"])
|
|
305
|
+
config["sysref"] = self.sysref
|
|
306
|
+
|
|
307
|
+
clk = self.vcxo * config["n2"] / config["r1"]
|
|
308
|
+
|
|
309
|
+
output_cfg = {}
|
|
310
|
+
for i, div in enumerate(out_dividers):
|
|
311
|
+
rate = clk / div
|
|
312
|
+
output_cfg[self._clk_names[i]] = {
|
|
313
|
+
"rate": rate,
|
|
314
|
+
"divider": div,
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
config["output_clocks"] = output_cfg
|
|
318
|
+
|
|
319
|
+
self._saved_solution = config
|
|
320
|
+
|
|
321
|
+
return config
|
|
322
|
+
|
|
323
|
+
def _setup_solver_constraints(self, vcxo: int) -> None:
|
|
324
|
+
"""Apply constraints to solver model.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
vcxo (int): VCXO frequency in hertz
|
|
328
|
+
|
|
329
|
+
Raises:
|
|
330
|
+
Exception: Unknown solver
|
|
331
|
+
"""
|
|
332
|
+
if not isinstance(vcxo, (float, int)):
|
|
333
|
+
vcxo = vcxo(self.model)
|
|
334
|
+
self.vcxo = vcxo["range"]
|
|
335
|
+
else:
|
|
336
|
+
self.vcxo = vcxo
|
|
337
|
+
|
|
338
|
+
self.config = {
|
|
339
|
+
"r1": self._convert_input(self._r1, "r1"),
|
|
340
|
+
"m1": self._convert_input(self._m1, "m1"),
|
|
341
|
+
"k": self._convert_input(self._k, "k"),
|
|
342
|
+
"n2": self._convert_input(self._n2, "n2"),
|
|
343
|
+
"a": self._convert_input(self._a, "a"),
|
|
344
|
+
"b": self._convert_input(self._b, "b"),
|
|
345
|
+
}
|
|
346
|
+
# self.config = {"r1": self.model.Var(integer=True, lb=1, ub=31, value=1)}
|
|
347
|
+
# self.config["m1"] = self.model.Var(integer=True, lb=3, ub=5, value=3)
|
|
348
|
+
# self.config["n2"] = self.model.Var(integer=True, lb=12, ub=255, value=12)
|
|
349
|
+
|
|
350
|
+
# PLL2 equations
|
|
351
|
+
self._add_equation(
|
|
352
|
+
[
|
|
353
|
+
self.vcxo / self.config["r1"] <= self.pfd_max,
|
|
354
|
+
self.vcxo / self.config["r1"] * self.config["m1"] * self.config["n2"]
|
|
355
|
+
<= self.vco_max,
|
|
356
|
+
self.vcxo / self.config["r1"] * self.config["m1"] * self.config["n2"]
|
|
357
|
+
>= self.vco_min,
|
|
358
|
+
4 * self.config["b"] + self.config["a"] >= 16,
|
|
359
|
+
4 * self.config["b"] + self.config["a"]
|
|
360
|
+
== self.config["m1"] * self.config["n2"],
|
|
361
|
+
]
|
|
362
|
+
)
|
|
363
|
+
# Objectives
|
|
364
|
+
if self.minimize_feedback_dividers:
|
|
365
|
+
if self.solver == "CPLEX":
|
|
366
|
+
self._add_objective(self.config["n2"])
|
|
367
|
+
# self.model.minimize(self.config["n2"])
|
|
368
|
+
elif self.solver == "gekko":
|
|
369
|
+
self.model.Obj(self.config["n2"])
|
|
370
|
+
else:
|
|
371
|
+
raise Exception("Unknown solver {}".format(self.solver))
|
|
372
|
+
# self.model.Obj(self.config["n2"] * self.config["m1"])
|
|
373
|
+
|
|
374
|
+
def _setup(self, vcxo: int) -> None:
|
|
375
|
+
# Setup clock chip internal constraints
|
|
376
|
+
|
|
377
|
+
# FIXME: ADD SPLIT m1 configuration support
|
|
378
|
+
|
|
379
|
+
# Setup clock chip internal constraints
|
|
380
|
+
if self.use_vcxo_double:
|
|
381
|
+
vcxo *= 2
|
|
382
|
+
self._setup_solver_constraints(vcxo)
|
|
383
|
+
|
|
384
|
+
# Add requested clocks to output constraints
|
|
385
|
+
self.config["out_dividers"] = []
|
|
386
|
+
self._clk_names = [] # Reset clock names
|
|
387
|
+
|
|
388
|
+
def _get_clock_constraint(
|
|
389
|
+
self, clk_name: str
|
|
390
|
+
) -> Union[int, float, CpoExpr, GK_Intermediate]:
|
|
391
|
+
"""Get abstract clock output.
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
clk_name (str): String of clock name
|
|
395
|
+
|
|
396
|
+
Returns:
|
|
397
|
+
(int or float or CpoExpr or GK_Intermediate): Abstract
|
|
398
|
+
or concrete clock reference
|
|
399
|
+
"""
|
|
400
|
+
od = self._convert_input(self._d, "d_" + str(clk_name))
|
|
401
|
+
self.config["out_dividers"].append(od)
|
|
402
|
+
self._clk_names.append(clk_name)
|
|
403
|
+
return self.vcxo / self.config["r1"] * self.config["n2"] / od
|
|
404
|
+
|
|
405
|
+
def set_requested_clocks(
|
|
406
|
+
self,
|
|
407
|
+
vcxo: int,
|
|
408
|
+
out_freqs: List,
|
|
409
|
+
clk_names: List[str],
|
|
410
|
+
) -> None:
|
|
411
|
+
"""Define necessary clocks to be generated in model.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
vcxo (int): VCXO frequency in hertz
|
|
415
|
+
out_freqs (List): list of required clocks to be output
|
|
416
|
+
clk_names (List[str]): list of strings of clock names
|
|
417
|
+
|
|
418
|
+
Raises:
|
|
419
|
+
Exception: If len(out_freqs) != len(clk_names)
|
|
420
|
+
"""
|
|
421
|
+
if len(clk_names) != len(out_freqs):
|
|
422
|
+
raise Exception("clk_names is not the same size as out_freqs")
|
|
423
|
+
|
|
424
|
+
# Setup clock chip internal constraints
|
|
425
|
+
self._setup(vcxo)
|
|
426
|
+
self._clk_names = clk_names
|
|
427
|
+
|
|
428
|
+
if self._sysref:
|
|
429
|
+
if self.sysref_external:
|
|
430
|
+
sysref_src = self.vcxo
|
|
431
|
+
else:
|
|
432
|
+
sysref_src = self.vcxo / self.config["r1"]
|
|
433
|
+
|
|
434
|
+
self._add_equation([sysref_src / (2 * self.config["k"]) == self._sysref])
|
|
435
|
+
|
|
436
|
+
# Add requested clocks to output constraints
|
|
437
|
+
for out_freq, name in zip(out_freqs, clk_names): # noqa: B905
|
|
438
|
+
# od = self.model.Var(integer=True, lb=1, ub=256, value=1)
|
|
439
|
+
od = self._convert_input(self._d, f"d_{name}_{out_freq}")
|
|
440
|
+
# od = self.model.sos1([n*n for n in range(1,9)])
|
|
441
|
+
self._add_equation(
|
|
442
|
+
[self.vcxo / self.config["r1"] * self.config["n2"] / od == out_freq]
|
|
443
|
+
)
|
|
444
|
+
self.config["out_dividers"].append(od)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# flake8: noqa
|
|
2
|
+
# pytype: skip-file
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from adijif.clocks.clock import clock
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ad9528_bf(clock):
|
|
9
|
+
"""Brute force methods for calculating clocks
|
|
10
|
+
|
|
11
|
+
These are currently meant for debug to compare against
|
|
12
|
+
the solver solutions
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def list_available_references(self, divider_set):
|
|
16
|
+
"""list_available_references: Based on config list possible
|
|
17
|
+
references that can be generated based on VCO and output
|
|
18
|
+
dividers
|
|
19
|
+
"""
|
|
20
|
+
# Check input
|
|
21
|
+
ref = {
|
|
22
|
+
"m1": 3,
|
|
23
|
+
"n2": 2,
|
|
24
|
+
"vco": 3000000000,
|
|
25
|
+
"r1": 24,
|
|
26
|
+
"required_output_divs": np.array([1.0]),
|
|
27
|
+
}
|
|
28
|
+
for key in ref:
|
|
29
|
+
if key not in divider_set:
|
|
30
|
+
raise Exception(
|
|
31
|
+
"Input must be of type dict with fields: " + str(ref.keys())
|
|
32
|
+
)
|
|
33
|
+
return [
|
|
34
|
+
divider_set["vco"] / divider_set["m1"] / div for div in self.d_available
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
def find_dividers(self, vcxo, required_output_rates, find=3):
|
|
38
|
+
if self.use_vcxo_double:
|
|
39
|
+
vcxo *= 2
|
|
40
|
+
|
|
41
|
+
mod = np.gcd.reduce(np.array(required_output_rates, dtype=int))
|
|
42
|
+
configs = []
|
|
43
|
+
|
|
44
|
+
for r1 in self.r1_available:
|
|
45
|
+
pfd = vcxo / r1
|
|
46
|
+
if pfd > self.pfd_max:
|
|
47
|
+
continue
|
|
48
|
+
for m1 in self.m1_available:
|
|
49
|
+
for n2 in self.n2_available:
|
|
50
|
+
# RECHECK THIS. NOT WELL DOCUMENTED
|
|
51
|
+
vco = pfd * m1 * n2
|
|
52
|
+
# Check bounds and integer
|
|
53
|
+
if (
|
|
54
|
+
vco > self.vco_min
|
|
55
|
+
and vco < self.vco_max
|
|
56
|
+
and (vco / m1) % mod == 0
|
|
57
|
+
):
|
|
58
|
+
required_output_divs = (vco / m1) / required_output_rates
|
|
59
|
+
if np.all(np.in1d(required_output_divs, self.d_available)):
|
|
60
|
+
configs.append(
|
|
61
|
+
{
|
|
62
|
+
"m1": m1,
|
|
63
|
+
"vco": vco,
|
|
64
|
+
"n2": n2,
|
|
65
|
+
"r1": r1,
|
|
66
|
+
"required_output_divs": required_output_divs,
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
return configs
|