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,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