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
adijif/clocks/clock.py ADDED
@@ -0,0 +1,153 @@
1
+ """Clock parent metaclass to maintain consistency for all clock chip."""
2
+
3
+ from abc import ABCMeta, abstractmethod
4
+ from typing import Dict, List, Union
5
+
6
+ from docplex.cp.solution import CpoSolveResult # type: ignore
7
+
8
+ from adijif.common import core
9
+ from adijif.draw import Layout, Node
10
+ from adijif.gekko_trans import gekko_translation
11
+
12
+
13
+ class clock(core, gekko_translation, metaclass=ABCMeta):
14
+ """Parent metaclass for all clock chip classes."""
15
+
16
+ @property
17
+ @abstractmethod
18
+ def find_dividers(self) -> Dict:
19
+ """Find all possible divider settings that validate config.
20
+
21
+ Raises:
22
+ NotImplementedError: Method not implemented
23
+ """
24
+ raise NotImplementedError # pragma: no cover
25
+
26
+ @property
27
+ @abstractmethod
28
+ def list_available_references(self) -> List[int]:
29
+ """Determine all references that can be generated.
30
+
31
+ Based on config list possible references that can be generated
32
+ based on VCO and output dividers
33
+
34
+ Raises:
35
+ NotImplementedError: Method not implemented
36
+ """
37
+ raise NotImplementedError # pragma: no cover
38
+
39
+ def _solve_gekko(self) -> bool:
40
+ """Local solve method for clock model.
41
+
42
+ Call model solver with correct arguments.
43
+
44
+ Returns:
45
+ bool: Always False
46
+ """
47
+ self.model.options.SOLVER = 1 # APOPT solver
48
+ self.model.solver_options = [
49
+ "minlp_maximum_iterations 1000", # minlp iterations with integer solution
50
+ "minlp_max_iter_with_int_sol 100", # treat minlp as nlp
51
+ "minlp_as_nlp 0", # nlp sub-problem max iterations
52
+ "nlp_maximum_iterations 500", # 1 = depth first, 2 = breadth first
53
+ "minlp_branch_method 1", # maximum deviation from whole number
54
+ "minlp_integer_tol 0", # covergence tolerance (MUST BE 0 TFC)
55
+ "minlp_gap_tol 0.1",
56
+ ]
57
+
58
+ self.model.solve(disp=False)
59
+ self.model.cleanup()
60
+ return False
61
+
62
+ # def _add_objective(self, sysrefs: List) -> None:
63
+ # pass
64
+
65
+ def _solve_cplex(self) -> CpoSolveResult:
66
+ self.solution = self.model.solve(LogVerbosity="Quiet")
67
+ if self.solution.solve_status not in ["Feasible", "Optimal"]:
68
+ raise Exception("Solution Not Found")
69
+ return self.solution
70
+
71
+ def solve(self) -> Union[None, CpoSolveResult]:
72
+ """Local solve method for clock model.
73
+
74
+ Call model solver with correct arguments.
75
+
76
+ Returns:
77
+ [None,CpoSolveResult]: When cplex solver is used CpoSolveResult is returned
78
+
79
+ Raises:
80
+ Exception: If solver is not valid
81
+
82
+ """
83
+ if self.solver == "gekko":
84
+ return self._solve_gekko()
85
+ elif self.solver == "CPLEX":
86
+ return self._solve_cplex()
87
+ else:
88
+ raise Exception(f"Unknown solver {self.solver}")
89
+
90
+ def draw(self, lo: Layout = None) -> str:
91
+ """Generic Draw converter model.
92
+
93
+ Args:
94
+ lo (Layout): Layout object to draw on
95
+
96
+ Returns:
97
+ str: Path to image file
98
+
99
+ Raises:
100
+ Exception: If no solution is saved
101
+ """
102
+ if not self._saved_solution:
103
+ raise Exception("No solution to draw. Must call solve first.")
104
+ clocks = self._saved_solution
105
+ system_draw = lo is not None
106
+ name = self.name.lower()
107
+
108
+ if not system_draw:
109
+ lo = Layout(f"{name} Example")
110
+ else:
111
+ assert isinstance(lo, Layout), "lo must be a Layout object"
112
+
113
+ ic_node = Node(self.name)
114
+ lo.add_node(ic_node)
115
+
116
+ # rate = clocks[f"{name}_ref_clk"]
117
+ # Find key with ending
118
+ ref_name = None
119
+ for key in clocks.keys():
120
+ if "vcxo" in key.lower():
121
+ ref_name = key
122
+ break
123
+ if ref_name is None:
124
+ raise Exception(f"No clock found for vcxo\n.Options: {clocks.keys()}")
125
+
126
+ if not system_draw:
127
+ ref_in = Node("REF_IN", ntype="input")
128
+ lo.add_node(ref_in)
129
+ else:
130
+ to_node = lo.get_node(ref_name)
131
+ from_node = lo.get_connection(to=to_node.name)
132
+ assert from_node, "No connection found"
133
+ assert isinstance(from_node, list), "Connection must be a list"
134
+ assert len(from_node) == 1, "Only one connection allowed"
135
+ ref_in = from_node[0]["from"]
136
+ # Remove to_node since it is not needed
137
+ lo.remove_node(to_node.name)
138
+
139
+ rate = clocks[ref_name]
140
+
141
+ lo.add_connection({"from": ref_in, "to": ic_node, "rate": rate})
142
+
143
+ # Add each output clock
144
+ for o_clk_name in clocks["output_clocks"]:
145
+ rate = clocks["output_clocks"][o_clk_name]["rate"]
146
+ # div = clocks['output_clocks'][o_clk_name]['divider']
147
+ if not system_draw:
148
+ out_node = Node(o_clk_name, ntype="out_clock_connected")
149
+ lo.add_node(out_node)
150
+ lo.add_connection({"from": ic_node, "to": out_node, "rate": rate})
151
+
152
+ if not system_draw:
153
+ return lo.draw()
@@ -0,0 +1,558 @@
1
+ """HMC7044 clock chip model."""
2
+
3
+ from typing import Dict, List, Union
4
+
5
+ from adijif.clocks.hmc7044_bf import hmc7044_bf
6
+
7
+ from adijif.solvers import CpoExpr, CpoModel # type: ignore # isort: skip # noqa: I202
8
+ from adijif.solvers import CpoSolveResult # type: ignore # isort: skip # noqa: I202
9
+ from adijif.solvers import GEKKO # type: ignore # isort: skip # noqa: I202
10
+ from adijif.solvers import GK_Intermediate # type: ignore # isort: skip # noqa: I202
11
+
12
+ from adijif.draw import Layout, Node # type: ignore # isort: skip # noqa: I202
13
+
14
+
15
+ class hmc7044(hmc7044_bf):
16
+ """HMC7044 clock chip model.
17
+
18
+ This model currently supports VCXO+PLL2 configurations
19
+ """
20
+
21
+ name = "HMC7044"
22
+
23
+ # Ranges
24
+ # r2_divider_min = 1
25
+ # r2_divider_max = 4095
26
+ r2_available = [*range(1, 4095 + 1)]
27
+
28
+ """ Output dividers """
29
+ d_available = [1, 3, 5, *range(2, 4095, 2)]
30
+ # When pulse generation is required (like for sysref) divder range
31
+ # is limited
32
+ d_syspulse_available = [*range(32, 4095, 2)]
33
+
34
+ # Defaults
35
+ _d: Union[int, List[int]] = [1, 3, 5, *range(2, 4095, 2)]
36
+ _r2: Union[int, List[int]] = [*range(1, 4095 + 1)]
37
+
38
+ # Limits
39
+ """ Internal limits """
40
+ vco_min = 2400e6
41
+ vco_max = 3200e6
42
+ pfd_max = 250e6
43
+ vcxo_min = 10e6
44
+ vcxo_max = 500e6
45
+
46
+ use_vcxo_double = True
47
+ vxco_doubler_available = [1, 2]
48
+ _vcxo_doubler = [1, 2]
49
+
50
+ minimize_feedback_dividers = True
51
+
52
+ # State management
53
+ _clk_names: List[str] = []
54
+
55
+ def __init__(
56
+ self, model: Union[GEKKO, CpoModel] = None, solver: str = "CPLEX"
57
+ ) -> None:
58
+ """Initialize HMC7044 clock chip model.
59
+
60
+ Args:
61
+ model (Model): Model to add constraints to
62
+ solver (str): Solver to use. Should be one of "CPLEX" or "gekko"
63
+
64
+ Raises:
65
+ Exception: Invalid solver
66
+ """
67
+ super(hmc7044, self).__init__(model, solver)
68
+ if solver == "gekko":
69
+ self.n2_available = [*range(8, 65535 + 1)]
70
+ self._n2 = [*range(8, 65535 + 1)]
71
+ elif solver == "CPLEX":
72
+ self.n2_available = [*range(8, 65535 + 1)]
73
+ self._n2 = [*range(8, 65535 + 1)]
74
+ else:
75
+ raise Exception("Unknown solver {}".format(solver))
76
+
77
+ @property
78
+ def d(self) -> Union[int, List[int]]:
79
+ """Output dividers.
80
+
81
+ Valid dividers are 1,2,3,4,5,6->(even)->4094
82
+
83
+ Returns:
84
+ int: Current allowable dividers
85
+ """
86
+ return self._d
87
+
88
+ @d.setter
89
+ def d(self, value: Union[int, List[int]]) -> None:
90
+ """Output dividers.
91
+
92
+ Valid dividers are 1,2,3,4,5,6->(even)->4094
93
+
94
+ Args:
95
+ value (int, list[int]): Allowable values for divider
96
+ """
97
+ self._check_in_range(value, self.d_available, "d")
98
+ self._d = value
99
+
100
+ @property
101
+ def n2(self) -> Union[int, List[int]]:
102
+ """n2: VCO feedback divider.
103
+
104
+ Valid dividers are 8->65536
105
+
106
+ Returns:
107
+ int: Current allowable dividers
108
+ """
109
+ return self._n2
110
+
111
+ @n2.setter
112
+ def n2(self, value: Union[int, List[int]]) -> None:
113
+ """VCO feedback divider.
114
+
115
+ Valid dividers are 8->65536
116
+
117
+ Args:
118
+ value (int, list[int]): Allowable values for divider
119
+ """
120
+ self._check_in_range(value, self.n2_available, "n2")
121
+ self._n2 = value
122
+
123
+ @property
124
+ def r2(self) -> Union[int, List[int]]:
125
+ """VCXO input dividers.
126
+
127
+ Valid dividers are 1->4096
128
+
129
+ Returns:
130
+ int: Current allowable dividers
131
+ """
132
+ return self._r2
133
+
134
+ @r2.setter
135
+ def r2(self, value: Union[int, List[int]]) -> None:
136
+ """VCXO input dividers.
137
+
138
+ Valid dividers are 1->4096
139
+
140
+ Args:
141
+ value (int, list[int]): Allowable values for divider
142
+ """
143
+ self._check_in_range(value, self.r2_available, "r2")
144
+ self._r2 = value
145
+
146
+ @property
147
+ def vxco_doubler(self) -> Union[int, List[int]]:
148
+ """VCXO doubler.
149
+
150
+ Valid dividers are 1,2
151
+
152
+ Returns:
153
+ int: Current doubler value
154
+ """
155
+ return self._vcxo_doubler
156
+
157
+ @vxco_doubler.setter
158
+ def vxco_doubler(self, value: Union[int, List[int]]) -> None:
159
+ """VCXO doubler.
160
+
161
+ Valid dividers are 1,2
162
+
163
+ Args:
164
+ value (int, list[int]): Allowable values for divider
165
+
166
+ """
167
+ self._check_in_range(value, self.vxco_doubler_available, "vxco_doubler")
168
+ self._vcxo_doubler = value
169
+
170
+ def _init_diagram(self) -> None:
171
+ """Initialize diagram for HMC7044 alone."""
172
+ self.ic_diagram_node = None
173
+ self._diagram_output_dividers = []
174
+
175
+ # lo = Layout("HMC7044 Example")
176
+
177
+ self.ic_diagram_node = Node("HMC7044")
178
+ # lo.add_node(root)
179
+
180
+ # External
181
+ # ref_in = Node("REF_IN", ntype="input")
182
+ # lo.add_node(ref_in)
183
+
184
+ vcxo_doubler = Node("VCXO Doubler", ntype="shell")
185
+ self.ic_diagram_node.add_child(vcxo_doubler)
186
+
187
+ # Inside the IC
188
+ r2_div = Node("R2", ntype="divider")
189
+ # r2_div.value = "2"
190
+ self.ic_diagram_node.add_child(r2_div)
191
+ pfd = Node("PFD", ntype="phase-frequency-detector")
192
+ self.ic_diagram_node.add_child(pfd)
193
+ lf = Node("LF", ntype="loop-filter")
194
+ self.ic_diagram_node.add_child(lf)
195
+ vco = Node("VCO", ntype="voltage-controlled-oscillator")
196
+ vco.shape = "circle"
197
+ self.ic_diagram_node.add_child(vco)
198
+ n2 = Node("N2", ntype="divider")
199
+ self.ic_diagram_node.add_child(n2)
200
+
201
+ out_dividers = Node("Output Dividers", ntype="shell")
202
+ # ds = 4
203
+ # out_divs = []
204
+ # for i in range(ds):
205
+ # div = Node(f"D{i+1}", ntype="divider")
206
+ # out_dividers.add_child(div)
207
+ # out_divs.append(div)
208
+
209
+ self.ic_diagram_node.add_child(out_dividers)
210
+
211
+ # Connections inside the IC
212
+ # lo.add_connection({"from": ref_in, "to": r2_div, 'rate': 125000000})
213
+ self.ic_diagram_node.add_connection({"from": vcxo_doubler, "to": r2_div})
214
+ self.ic_diagram_node.add_connection(
215
+ {"from": r2_div, "to": pfd, "rate": 125000000 / 2}
216
+ )
217
+ self.ic_diagram_node.add_connection({"from": pfd, "to": lf})
218
+ self.ic_diagram_node.add_connection({"from": lf, "to": vco})
219
+ self.ic_diagram_node.add_connection({"from": vco, "to": n2})
220
+ self.ic_diagram_node.add_connection({"from": n2, "to": pfd})
221
+
222
+ self.ic_diagram_node.add_connection(
223
+ {"from": vco, "to": out_dividers, "rate": 4000000000}
224
+ )
225
+ # for div in out_divs:
226
+ # self.ic_diagram_node.add_connection({"from": out_dividers, "to": div})
227
+ # # root.add_connection({"from": vco, "to": div})
228
+
229
+ def _update_diagram(self, config: Dict) -> None:
230
+ """Update diagram with configuration.
231
+
232
+ Args:
233
+ config (Dict): Configuration dictionary
234
+
235
+ Raises:
236
+ Exception: If key is not D followed by a number
237
+ """
238
+ # Add output dividers
239
+ keys = config.keys()
240
+ output_dividers = self.ic_diagram_node.get_child("Output Dividers")
241
+ for key in keys:
242
+ if key.startswith("D"):
243
+ div = Node(key, ntype="divider")
244
+ output_dividers.add_child(div)
245
+ self.ic_diagram_node.add_connection(
246
+ {"from": output_dividers, "to": div}
247
+ )
248
+ else:
249
+ raise Exception(
250
+ f"Unknown key {key}. Must be of for DX where X is a number"
251
+ )
252
+
253
+ def draw(self, lo: Layout = None) -> str:
254
+ """Draw diagram in d2 language for IC alone with reference clock.
255
+
256
+ Args:
257
+ lo: Layout for drawing
258
+
259
+ Returns:
260
+ str: Diagram in d2 language
261
+
262
+ Raises:
263
+ Exception: If no solution is saved
264
+ """
265
+ if not self._saved_solution:
266
+ raise Exception("No solution to draw. Must call solve first.")
267
+
268
+ system_draw = lo is not None
269
+ if not system_draw:
270
+ lo = Layout("HMC7044 Example")
271
+ else:
272
+ # Verify lo is a Layout object
273
+ assert isinstance(lo, Layout), "lo must be a Layout object"
274
+ lo.add_node(self.ic_diagram_node)
275
+
276
+ ref_in = Node("REF_IN", ntype="input")
277
+ lo.add_node(ref_in)
278
+ vcxo_double = self.ic_diagram_node.get_child("VCXO Doubler")
279
+ lo.add_connection(
280
+ {"from": ref_in, "to": vcxo_double, "rate": self._saved_solution["vcxo"]}
281
+ )
282
+
283
+ # Update Node values
284
+ node = self.ic_diagram_node.get_child("VCXO Doubler")
285
+ node.value = str(self._saved_solution["vcxo_doubler"])
286
+ node = self.ic_diagram_node.get_child("R2")
287
+ node.value = str(self._saved_solution["r2"])
288
+ node = self.ic_diagram_node.get_child("N2")
289
+ node.value = str(self._saved_solution["n2"])
290
+
291
+ # Update VCXO Doubler to R2
292
+ # con = self.ic_diagram_node.get_connection("VCXO Doubler", "R2")
293
+ rate = self._saved_solution["vcxo_doubler"] * self._saved_solution["vcxo"]
294
+ self.ic_diagram_node.update_connection("VCXO Doubler", "R2", rate)
295
+
296
+ # Update R2 to PFD
297
+ # con = self.ic_diagram_node.get_connection("R2", "PFD")
298
+ rate = (
299
+ self._saved_solution["vcxo"]
300
+ * self._saved_solution["vcxo_doubler"]
301
+ / self._saved_solution["r2"]
302
+ )
303
+ self.ic_diagram_node.update_connection("R2", "PFD", rate)
304
+
305
+ # Update VCO
306
+ # con = self.ic_diagram_node.get_connection("VCO", "Output Dividers")
307
+ self.ic_diagram_node.update_connection(
308
+ "VCO", "Output Dividers", self._saved_solution["vco"]
309
+ )
310
+
311
+ # Update diagram with dividers and rates
312
+ d = 0
313
+ output_dividers = self.ic_diagram_node.get_child("Output Dividers")
314
+
315
+ for key, val in self._saved_solution["output_clocks"].items():
316
+ clk_node = Node(key, ntype="divider")
317
+ div_value = val["divider"]
318
+ div = output_dividers.get_child(f"D{d}")
319
+ div.value = str(div_value)
320
+ d += 1
321
+ lo.add_node(clk_node)
322
+ lo.add_connection({"from": div, "to": clk_node, "rate": val["rate"]})
323
+
324
+ if system_draw:
325
+ return lo.draw()
326
+
327
+ return lo.draw()
328
+
329
+ def get_config(self, solution: CpoSolveResult = None) -> Dict:
330
+ """Extract configurations from solver results.
331
+
332
+ Collect internal clock chip configuration and output clock definitions
333
+ leading to connected devices (converters, FPGAs)
334
+
335
+ Args:
336
+ solution (CpoSolveResult): CPlex solution. Only needed for CPlex solver
337
+
338
+ Returns:
339
+ Dict: Dictionary of clocking rates and dividers for configuration
340
+
341
+ Raises:
342
+ Exception: If solver is not called first
343
+ """
344
+ if not self._clk_names:
345
+ raise Exception("set_requested_clocks must be called before get_config")
346
+
347
+ if solution:
348
+ self.solution = solution
349
+
350
+ out_dividers = [self._get_val(x) for x in self.config["out_dividers"]]
351
+
352
+ config: Dict = {
353
+ "r2": self._get_val(self.config["r2"]),
354
+ "n2": self._get_val(self.config["n2"]),
355
+ "out_dividers": out_dividers,
356
+ "output_clocks": [],
357
+ }
358
+
359
+ if self.vcxo_i:
360
+ vcxo = self._get_val(self.vcxo_i["range"])
361
+ self.vcxo = vcxo
362
+
363
+ clk = self.vcxo / config["r2"] * config["n2"]
364
+
365
+ output_cfg = {}
366
+ vd = self._get_val(self.config["vcxo_doubler"])
367
+ for i, div in enumerate(out_dividers):
368
+ rate = vd * clk / div
369
+ output_cfg[self._clk_names[i]] = {"rate": rate, "divider": div}
370
+
371
+ config["output_clocks"] = output_cfg
372
+ config["vco"] = clk * vd
373
+ config["vcxo"] = self.vcxo
374
+ config["vcxo_doubler"] = vd
375
+
376
+ self._saved_solution = config
377
+
378
+ return config
379
+
380
+ def _setup_solver_constraints(self, vcxo: int) -> None:
381
+ """Apply constraints to solver model.
382
+
383
+ Args:
384
+ vcxo (int): VCXO frequency in hertz
385
+
386
+ Raises:
387
+ Exception: Invalid solver
388
+ """
389
+ self.vcxo = vcxo
390
+
391
+ if self.solver == "gekko":
392
+ self.config = {"r2": self.model.Var(integer=True, lb=1, ub=4095, value=1)}
393
+ self.config["n2"] = self.model.Var(integer=True, lb=8, ub=4095)
394
+ if isinstance(vcxo, (int, float)):
395
+ vcxo_var = self.model.Const(int(vcxo))
396
+ else:
397
+ vcxo_var = vcxo
398
+ self.config["vcxo_doubler"] = self.model.sos1([1, 2])
399
+ self.config["vcxod"] = self.model.Intermediate(
400
+ self.config["vcxo_doubler"] * vcxo_var
401
+ )
402
+ elif self.solver == "CPLEX":
403
+ self.config = {
404
+ "r2": self._convert_input(self._r2, "r2"),
405
+ "n2": self._convert_input(self._n2, "n2"),
406
+ }
407
+ self.config["vcxo_doubler"] = self._convert_input(
408
+ self._vcxo_doubler, "vcxo_doubler"
409
+ )
410
+ self.config["vcxod"] = self._add_intermediate(
411
+ self.config["vcxo_doubler"] * vcxo
412
+ )
413
+ else:
414
+ raise Exception("Unknown solver {}".format(self.solver))
415
+
416
+ # PLL2 equations
417
+ self._add_equation(
418
+ [
419
+ self.config["vcxod"] <= self.pfd_max * self.config["r2"],
420
+ self.config["vcxod"] * self.config["n2"]
421
+ <= self.vco_max * self.config["r2"],
422
+ self.config["vcxod"] * self.config["n2"]
423
+ >= self.vco_min * self.config["r2"],
424
+ ]
425
+ )
426
+
427
+ # Objectives
428
+ if self.minimize_feedback_dividers:
429
+ if self.solver == "CPLEX":
430
+ self._add_objective(self.config["r2"])
431
+ # self.model.minimize(self.config["r2"])
432
+ elif self.solver == "gekko":
433
+ self.model.Obj(self.config["r2"])
434
+ else:
435
+ raise Exception("Unknown solver {}".format(self.solver))
436
+
437
+ def _setup(self, vcxo: int) -> None:
438
+ # Setup clock chip internal constraints
439
+
440
+ # FIXME: ADD SPLIT m1 configuration support
441
+
442
+ # Convert VCXO into intermediate in case we have range type
443
+ if type(vcxo) not in [int, float]:
444
+ self.vcxo_i = vcxo(self.model)
445
+ vcxo = self.vcxo_i["range"]
446
+ else:
447
+ self.vcxo_i = False
448
+
449
+ self._setup_solver_constraints(vcxo)
450
+
451
+ # Add requested clocks to output constraints
452
+ self.config["out_dividers"] = []
453
+ self._clk_names = [] # Reset
454
+
455
+ def _get_clock_constraint(
456
+ self, clk_name: List[str]
457
+ ) -> Union[int, float, CpoExpr, GK_Intermediate]:
458
+ """Get abstract clock output.
459
+
460
+ Args:
461
+ clk_name (str): String of clock name
462
+
463
+ Returns:
464
+ (int or float or CpoExpr or GK_Intermediate): Abstract
465
+ or concrete clock reference
466
+
467
+ Raises:
468
+ Exception: Invalid solver
469
+ """
470
+ if self.solver == "gekko":
471
+
472
+ __d = self._d if isinstance(self._d, list) else [self._d]
473
+
474
+ if __d.sort() != self.d_available.sort():
475
+ raise Exception("For solver gekko d is not configurable for HMC7044")
476
+
477
+ even = self.model.Var(integer=True, lb=3, ub=2047)
478
+ odd = self.model.Intermediate(even * 2)
479
+ od = self.model.sos1([1, 2, 3, 4, 5, odd])
480
+
481
+ elif self.solver == "CPLEX":
482
+ od = self._convert_input(self._d, "d_" + str(clk_name))
483
+ else:
484
+ raise Exception("Unknown solver {}".format(self.solver))
485
+
486
+ # Update diagram to include new divider
487
+ d_n = len(self.config["out_dividers"])
488
+ self._update_diagram({f"D{d_n}": od})
489
+
490
+ self._clk_names.append(clk_name)
491
+
492
+ self.config["out_dividers"].append(od)
493
+ return self.config["vcxod"] / self.config["r2"] * self.config["n2"] / od
494
+
495
+ def set_requested_clocks(
496
+ self, vcxo: int, out_freqs: List, clk_names: List[str]
497
+ ) -> None:
498
+ """Define necessary clocks to be generated in model.
499
+
500
+ Args:
501
+ vcxo (int): VCXO frequency in hertz
502
+ out_freqs (List): list of required clocks to be output
503
+ clk_names (List[str]): list of strings of clock names
504
+
505
+ Raises:
506
+ Exception: If len(out_freqs) != len(clk_names)
507
+ """
508
+ if len(clk_names) != len(out_freqs):
509
+ raise Exception("clk_names is not the same size as out_freqs")
510
+
511
+ # Setup clock chip internal constraints
512
+ self._setup(vcxo)
513
+ self._clk_names = clk_names
514
+ # if type(self.vcxo) not in [int,float]:
515
+ # vcxo = self.vcxo['range']
516
+
517
+ self._saved_solution = None
518
+
519
+ # Add requested clocks to output constraints
520
+ for d_n, out_freq in enumerate(out_freqs):
521
+
522
+ if self.solver == "gekko":
523
+ __d = self._d if isinstance(self._d, list) else [self._d]
524
+ if __d.sort() != self.d_available.sort():
525
+ raise Exception(
526
+ "For solver gekko d is not configurable for HMC7044"
527
+ )
528
+
529
+ # even = self.model.Var(integer=True, lb=3, ub=2047)
530
+ # odd = self.model.Intermediate(even * 2)
531
+ # od = self.model.sos1([1, 2, 3, 4, 5, odd])
532
+
533
+ # Since d is so disjoint it is very annoying to solve.
534
+ even = self.model.Var(integer=True, lb=1, ub=4094 // 2)
535
+
536
+ # odd = self.model.sos1([1, 3, 5])
537
+ odd_i = self.model.Var(integer=True, lb=0, ub=2)
538
+ odd = self.model.Intermediate(1 + odd_i * 2)
539
+
540
+ eo = self.model.Var(integer=True, lb=0, ub=1)
541
+ od = self.model.Intermediate(eo * odd + (1 - eo) * even * 2)
542
+
543
+ elif self.solver == "CPLEX":
544
+ od = self._convert_input(self._d, f"d_{out_freq}_{d_n}")
545
+
546
+ self._add_equation(
547
+ [
548
+ self.config["vcxod"] * self.config["n2"]
549
+ == out_freq * self.config["r2"] * od
550
+ ]
551
+ )
552
+ self.config["out_dividers"].append(od)
553
+
554
+ # Update diagram to include new divider
555
+ self._update_diagram({f"D{d_n}": od})
556
+
557
+ # Objectives
558
+ # self.model.Obj(-1*eo) # Favor even dividers