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,184 @@
1
+ """Drawing features for AD9680."""
2
+
3
+ from typing import Dict
4
+
5
+ from adijif.draw import Layout, Node # type: ignore # isort: skip # noqa: I202
6
+
7
+
8
+ class ad9680_draw:
9
+ """AD9680 drawing features."""
10
+
11
+ _system_draw = False
12
+ show_rates = True
13
+
14
+ def _init_diagram(self) -> None:
15
+ """Initialize diagram for AD9680 alone."""
16
+ self.ic_diagram_node = None
17
+ self._diagram_output_dividers = []
18
+
19
+ self.ic_diagram_node = Node("AD9680")
20
+
21
+ # External
22
+ # ref_in = Node("REF_IN", ntype="input")
23
+ # lo.add_node(ref_in)
24
+
25
+ crossbar = Node("Crossbar", ntype="crossbar")
26
+ self.ic_diagram_node.add_child(crossbar)
27
+ for adc in range(2):
28
+ adc_node = Node(f"ADC{adc}", ntype="adc")
29
+ self.ic_diagram_node.add_child(adc_node)
30
+ adc_node.shape = "parallelogram"
31
+ self.ic_diagram_node.add_connection(
32
+ {"from": adc_node, "to": crossbar, "type": "data"}
33
+ )
34
+
35
+ for ddc in range(4):
36
+ ddc_node = Node(f"DDC{ddc}", ntype="ddc")
37
+ self.ic_diagram_node.add_child(ddc_node)
38
+ self.ic_diagram_node.add_connection(
39
+ {"from": crossbar, "to": ddc_node, "type": "data"}
40
+ )
41
+
42
+ jesd204_framer = Node("JESD204 Framer", ntype="jesd204framer")
43
+ self.ic_diagram_node.add_child(jesd204_framer)
44
+
45
+ for ddc in range(4):
46
+ ddc = self.ic_diagram_node.get_child(f"DDC{ddc}")
47
+ self.ic_diagram_node.add_connection(
48
+ {"from": ddc, "to": jesd204_framer, "type": "data"}
49
+ )
50
+
51
+ def _update_diagram(self, config: Dict) -> None:
52
+ """Update diagram with configuration.
53
+
54
+ Args:
55
+ config (Dict): Configuration dictionary
56
+
57
+ Raises:
58
+ Exception: If key is not D followed by a number
59
+ """
60
+ # Add output dividers
61
+ keys = config.keys()
62
+ output_dividers = self.ic_diagram_node.get_child("Output Dividers")
63
+ for key in keys:
64
+ if key.startswith("D"):
65
+ div = Node(key, ntype="divider")
66
+ output_dividers.add_child(div)
67
+ self.ic_diagram_node.add_connection(
68
+ {"from": output_dividers, "to": div}
69
+ )
70
+ else:
71
+ raise Exception(
72
+ f"Unknown key {key}. Must be of for DX where X is a number"
73
+ )
74
+
75
+ def draw(
76
+ self, clocks: Dict, lo: Layout = None, clock_chip_node: Node = None
77
+ ) -> str:
78
+ """Draw diagram in d2 language for IC alone with reference clock.
79
+
80
+ Args:
81
+ clocks (Dict): Dictionary of clocks
82
+ lo (Layout): Layout object to add to. Defaults to None.
83
+ clock_chip_node (Node): Node to connect to. Defaults to None.
84
+
85
+ Returns:
86
+ str: Diagram in d2 language
87
+
88
+ Raises:
89
+ Exception: If no solution is saved
90
+ """
91
+ if not self._saved_solution:
92
+ raise Exception("No solution to draw. Must call solve first.")
93
+
94
+ system_draw = lo is not None
95
+
96
+ if not system_draw:
97
+ lo = Layout("AD9680 Example")
98
+ lo.show_rates = self.show_rates
99
+ else:
100
+ # Verify lo is a Layout object
101
+ assert isinstance(lo, Layout), "lo must be a Layout object"
102
+ lo.add_node(self.ic_diagram_node)
103
+
104
+ static_options = self.get_config()
105
+
106
+ if not system_draw:
107
+ ref_in = Node("REF_IN", ntype="input")
108
+ lo.add_node(ref_in)
109
+ else:
110
+ to_node = lo.get_node("AD9680_ref_clk")
111
+ # Locate node connected to this one
112
+ from_node = lo.get_connection(to=to_node.name)
113
+ assert from_node, "No connection found"
114
+ assert isinstance(from_node, list), "Connection must be a list"
115
+ assert len(from_node) == 1, "Only one connection allowed"
116
+ ref_in = from_node[0]["from"]
117
+ # Remove to_node since it is not needed
118
+ lo.remove_node(to_node.name)
119
+
120
+ for i in range(2):
121
+ adc = self.ic_diagram_node.get_child(f"ADC{i}")
122
+ lo.add_connection(
123
+ {"from": ref_in, "to": adc, "rate": clocks["AD9680_ref_clk"]}
124
+ )
125
+
126
+ # Update Node values
127
+ for ddc in range(4):
128
+ rate = clocks["AD9680_ref_clk"]
129
+ self.ic_diagram_node.update_connection("Crossbar", f"DDC{ddc}", rate)
130
+
131
+ ddc_node = self.ic_diagram_node.get_child(f"DDC{ddc}")
132
+ ddc_node.value = str(static_options["decimation"])
133
+ drate = rate / static_options["decimation"]
134
+
135
+ self.ic_diagram_node.update_connection(f"DDC{ddc}", "JESD204 Framer", drate)
136
+
137
+ # Connect clock to framer
138
+ if not system_draw:
139
+ sysref_in = Node("SYSREF_IN", ntype="input")
140
+
141
+ lo.add_connection(
142
+ {
143
+ "from": sysref_in,
144
+ "to": self.ic_diagram_node.get_child("JESD204 Framer"),
145
+ "rate": clocks["AD9680_sysref"],
146
+ }
147
+ )
148
+ else:
149
+ to_node = lo.get_node("AD9680_sysref")
150
+ # Locate node connected to this one
151
+ from_node = lo.get_connection(to=to_node.name)
152
+ assert from_node, "No connection found"
153
+ assert isinstance(from_node, list), "Connection must be a list"
154
+ assert len(from_node) == 1, "Only one connection allowed"
155
+ sysref_in = from_node[0]["from"]
156
+ # Remove to_node since it is not needed
157
+ lo.remove_node(to_node.name)
158
+
159
+ lo.add_connection(
160
+ {
161
+ "from": sysref_in,
162
+ "to": self.ic_diagram_node.get_child("JESD204 Framer"),
163
+ "rate": clocks["AD9680_sysref"],
164
+ }
165
+ )
166
+
167
+ # Connect Remote Deframer
168
+ remote_deframer = Node("JESD204 Deframer", ntype="deframer")
169
+ lo.add_node(remote_deframer)
170
+
171
+ # Add connect for each lane
172
+ for _ in range(self.L):
173
+ lane_rate = self.bit_clock
174
+ lo.add_connection(
175
+ {
176
+ "from": self.ic_diagram_node.get_child("JESD204 Framer"),
177
+ "to": remote_deframer,
178
+ "rate": lane_rate,
179
+ "type": "data",
180
+ }
181
+ )
182
+
183
+ if not system_draw:
184
+ return lo.draw()
@@ -0,0 +1,83 @@
1
+ """Converter base meta class for all converter clocking models."""
2
+
3
+ from abc import ABCMeta, abstractmethod
4
+ from typing import List, Union
5
+
6
+ from adijif.converters.converter import converter
7
+
8
+
9
+ class adc(converter, metaclass=ABCMeta):
10
+ """ADC base meta class used to enforce all ADC classes.
11
+
12
+ This class should be inherited from for all ADCs and transceivers.
13
+
14
+ """
15
+
16
+ def _check_valid_internal_configuration(self) -> None:
17
+ """Verify current internal clocking configuration for part is valid.
18
+
19
+ Raises:
20
+ Exception: Invalid clocking configuration
21
+ """
22
+ if self.decimation * self.sample_clock > self.converter_clock_max:
23
+ raise Exception(
24
+ "ADC rate too fast for configuration {}".format(
25
+ self.decimation * self.sample_clock
26
+ )
27
+ )
28
+ if self.decimation * self.sample_clock < self.converter_clock_min:
29
+ raise Exception(
30
+ "ADC rate too slow for configuration {}".format(
31
+ self.decimation * self.sample_clock
32
+ )
33
+ )
34
+
35
+ @property
36
+ @abstractmethod
37
+ def decimation_available(self) -> List[int]:
38
+ """Decimation settings available.
39
+
40
+ Must be a list or None
41
+
42
+ Raises:
43
+ NotImplementedError: If child classes do not implement method/property
44
+ """
45
+ raise NotImplementedError # pragma: no cover
46
+
47
+ _decimation = 1
48
+
49
+ @property
50
+ def decimation(self) -> Union[int, float]:
51
+ """Decimation between DAC and JESD framer. If none device is not an ADC.
52
+
53
+ Generally a multiple of 2
54
+
55
+ Returns:
56
+ int: decimation value
57
+ """
58
+ return self._decimation
59
+
60
+ @decimation.setter
61
+ def decimation(self, value: int) -> None:
62
+ """Decimation between DAC and JESD framer. If none device is not an ADC.
63
+
64
+ Args:
65
+ value (int): decimation
66
+
67
+ Raises:
68
+ Exception: decimation not an integer or not in range
69
+ """
70
+ if int(value) != value:
71
+ raise Exception("decimation_available must be an integer")
72
+ if value not in self.decimation_available:
73
+ raise Exception("decimation_available not in range for device")
74
+ self._decimation = value
75
+
76
+ @property
77
+ def converter_clock(self) -> Union[int, float]:
78
+ """Get rate of converter in samples per second.
79
+
80
+ Returns:
81
+ float: converter clock rate in samples per second
82
+ """
83
+ return self.sample_clock * self.decimation
@@ -0,0 +1,426 @@
1
+ """ADRV9009 transceiver clocking model."""
2
+
3
+ from abc import ABCMeta
4
+ from typing import Any, Dict, List, Union
5
+
6
+ import numpy as np
7
+
8
+ from adijif.converters.adrv9009_bf import adrv9009_bf
9
+
10
+ from ..solvers import CpoModel # type: ignore # noqa: I202,BLK100
11
+ from ..solvers import GEKKO, CpoSolveResult
12
+ from .adc import adc
13
+ from .adrv9009_util import quick_configuration_modes_rx # type: ignore
14
+ from .adrv9009_util import _extra_jesd_check, quick_configuration_modes_tx
15
+ from .converter import converter
16
+ from .dac import dac
17
+
18
+ # References
19
+ # https://ez.analog.com/wide-band-rf-transceivers/design-support-adrv9008-1-adrv9008-2-adrv9009/f/q-a/103757/adrv9009-clock-configuration/308013#308013
20
+
21
+
22
+ class adrv9009_core(converter, metaclass=ABCMeta):
23
+ """ADRV9009 transceiver clocking model.
24
+
25
+ This model manage the JESD configuration and input clock constraints.
26
+ External LO constraints are not modeled.
27
+
28
+ Clocking: ADRV9009 uses onboard PLLs to generate the JESD clocks
29
+
30
+ Lane Rate = I/Q Sample Rate * M * Np * (10 / 8) / L
31
+ Lane Rate = sample_clock * M * Np * (10 / 8) / L
32
+ """
33
+
34
+ device_clock_available = None # FIXME
35
+ device_clock_ranges = None # FIXME
36
+
37
+ name = "ADRV9009"
38
+
39
+ # JESD configurations
40
+ quick_configuration_modes = None # FIXME
41
+ available_jesd_modes = ["jesd204b"]
42
+ M_available = [1, 2, 4]
43
+ L_available = [1, 2, 3, 4, 6, 8]
44
+ N_available = [12, 16]
45
+ Np_available = [12, 16, 24]
46
+ F_available = [1, 2, 3, 4, 8]
47
+ S_available = [1] # FIXME?
48
+ K_available = [*np.arange(1, 32 + 1)]
49
+ CS_available = [0]
50
+ CF_available = [0]
51
+
52
+ # Clock constraints
53
+ converter_clock_min = 39.063e6 * 8
54
+ converter_clock_max = 12288e6
55
+
56
+ sample_clock_min = 39.063e6
57
+ sample_clock_max = 491520000
58
+
59
+ device_clock_min = 10e6
60
+ device_clock_max = 1e9
61
+
62
+ clocking_option_available = ["integrated_pll"]
63
+ _clocking_option = "integrated_pll"
64
+
65
+ # Divider ranges
66
+ input_clock_dividers_available = [1 / 2, 1, 2, 4, 8, 16]
67
+ input_clock_dividers_times2_available = [1, 2, 4, 8, 16, 32]
68
+
69
+ _lmfc_divisor_sysref_available = [*range(1, 20)]
70
+
71
+ # Unused
72
+ max_rx_sample_clock = 250e6
73
+ max_tx_sample_clock = 500e6
74
+ max_obs_sample_clock = 500e6
75
+
76
+ def _check_valid_internal_configuration(self) -> None:
77
+ # FIXME
78
+ pass
79
+
80
+ def get_required_clock_names(self) -> List[str]:
81
+ """Get list of strings of names of requested clocks.
82
+
83
+ This list of names is for the clocks defined by
84
+ get_required_clocks
85
+
86
+ Returns:
87
+ List[str]: List of strings of clock names mapped by get_required_clocks
88
+ """
89
+ return ["adrv9009_ref_clk", "adrv9009_sysref"]
90
+
91
+ def get_config(self, solution: CpoSolveResult = None) -> Dict:
92
+ """Extract configurations from solver results.
93
+
94
+ Collect internal converter configuration and output clock definitions
95
+ leading to connected devices (clock chips, FPGAs)
96
+
97
+ Args:
98
+ solution (CpoSolveResult): CPlex solution. Only needed for CPlex solver
99
+
100
+ Returns:
101
+ Dict: Dictionary of clocking rates and dividers for configuration
102
+ """
103
+ if solution:
104
+ self.solution = solution
105
+ return {}
106
+
107
+
108
+ class adrv9009_clock_common(adrv9009_core, adrv9009_bf):
109
+ """ADRV9009 class managing common singleton (Rx,Tx) methods."""
110
+
111
+ def _check_valid_jesd_mode(self) -> None:
112
+ """Verify current JESD configuration for part is valid."""
113
+ _extra_jesd_check(self)
114
+ return super()._check_valid_jesd_mode()
115
+
116
+ def get_config(self, solution: CpoSolveResult = None) -> Dict:
117
+ """Extract configurations from solver results.
118
+
119
+ Collect internal converter configuration and output clock definitions
120
+ leading to connected devices (clock chips, FPGAs)
121
+
122
+ Args:
123
+ solution (CpoSolveResult): CPlex solution. Only needed for CPlex solver
124
+
125
+ Returns:
126
+ Dict: Dictionary of clocking rates and dividers for configuration
127
+ """
128
+ return {"clocking_option": self.clocking_option}
129
+
130
+ def _gekko_get_required_clocks(self) -> List[Dict]:
131
+ possible_sysrefs = []
132
+ for n in range(1, 20):
133
+ r = self.multiframe_clock / (n * n)
134
+ if r == int(r):
135
+ possible_sysrefs.append(r)
136
+
137
+ self.config = {"sysref": self.model.sos1(possible_sysrefs)}
138
+ self.model.Obj(self.config["sysref"])
139
+
140
+ possible_device_clocks = []
141
+ for div in self.input_clock_dividers_available:
142
+ dev_clock = self.sample_clock / div
143
+ if self.device_clock_min <= dev_clock <= self.device_clock_max:
144
+ possible_device_clocks.append(dev_clock)
145
+
146
+ self.config["ref_clk"] = self.model.sos1(possible_device_clocks)
147
+ # self.model.Obj(-1 * self.config["device_clock"])
148
+
149
+ return [self.config["ref_clk"], self.config["sysref"]]
150
+
151
+ def get_required_clocks(self) -> List[Dict]:
152
+ """Generate list of required clocks.
153
+
154
+ For ADRV9009 this will contain:
155
+ [device clock requirement SOS, sysref requirement SOS]
156
+
157
+ Returns:
158
+ list[dict]: List of dictionaries of solver variables, equations, and constants
159
+ """
160
+ if self.solver == "gekko":
161
+ return self._gekko_get_required_clocks()
162
+ self.config = {}
163
+ self.config["lmfc_divisor_sysref"] = self._convert_input(
164
+ [*range(1, 20)], name="lmfc_divisor_sysref"
165
+ )
166
+
167
+ self.config["input_clock_divider_x2"] = self._convert_input(
168
+ self.input_clock_dividers_times2_available
169
+ )
170
+ self.config["ref_clk"] = self._add_intermediate(
171
+ self.sample_clock / self.config["input_clock_divider_x2"]
172
+ )
173
+ self.config["sysref"] = self._add_intermediate(
174
+ self.multiframe_clock
175
+ / (self.config["lmfc_divisor_sysref"] * self.config["lmfc_divisor_sysref"])
176
+ )
177
+
178
+ self._add_equation(
179
+ [
180
+ self.device_clock_min <= self.config["ref_clk"],
181
+ self.config["ref_clk"] <= self.device_clock_max,
182
+ ]
183
+ )
184
+
185
+ return [self.config["ref_clk"], self.config["sysref"]]
186
+
187
+
188
+ class adrv9009_rx(adc, adrv9009_clock_common, adrv9009_core):
189
+ """ADRV9009 Receive model."""
190
+
191
+ quick_configuration_modes = {"jesd204b": quick_configuration_modes_rx}
192
+ name = "ADRV9009_RX"
193
+ converter_type = "adc"
194
+
195
+ # JESD configurations
196
+ K_available = [*np.arange(1, 32 + 1)]
197
+ L_available = [1, 2, 4]
198
+ M_available = [1, 2, 4]
199
+ N_available = [12, 14, 16, 24]
200
+ Np_available = [12, 16, 24]
201
+ F_available = [
202
+ 1,
203
+ 2,
204
+ 3,
205
+ 4,
206
+ 6,
207
+ 8,
208
+ ]
209
+ CS_available = [0]
210
+ CF_available = [0]
211
+ S_available = [1, 2, 4]
212
+
213
+ # Clock constraints
214
+ bit_clock_min_available = {"jesd204b": 3.6864e9}
215
+ bit_clock_max_available = {"jesd204b": 12.288e9}
216
+
217
+ _decimation = 8
218
+ decimation_available = [4, 5, 8, 10, 16, 20, 32, 40]
219
+ """
220
+ ADRV9009 Rx decimation stages.
221
+
222
+ .. code-block:: none
223
+
224
+ +-----------+
225
+ +-----------+ Dec 5 (5) +---------+
226
+ | +-----------+ |
227
+ | |
228
+ | +----------+ +----------+ | +------------+ +--------------+
229
+ >---+---+ RHB3 (2) +---+ RHB2 (2) +---+--+ RHB1 (1,2) +---+ RFIR (1,2,4) +
230
+ +----------+ +----------+ +------------+ +--------------+
231
+ """
232
+
233
+ def __init__(self, *args: Any, **kwargs: Any) -> None: # noqa: ANN401
234
+ """Initialize ADRV9009-RX class.
235
+
236
+ Objects will default to mode and sample_clock.
237
+
238
+ Args:
239
+ *args (Any): Pass through arguments
240
+ **kwargs (Any): Pass through keyword arguments
241
+ """
242
+ # Set default mode
243
+ self.set_quick_configuration_mode(str(16))
244
+ # self.K = 32
245
+ self.sample_clock = int(122.88e6)
246
+ super().__init__(*args, **kwargs)
247
+ # self._init_diagram()
248
+
249
+ def get_required_clock_names(self) -> List[str]:
250
+ """Get list of strings of names of requested clocks.
251
+
252
+ This list of names is for the clocks defined by
253
+ get_required_clocks
254
+
255
+ Returns:
256
+ List[str]: List of strings of clock names mapped by get_required_clocks
257
+ """
258
+ return ["adrv9009_rx_ref_clk", "adrv9009_rx_sysref"]
259
+
260
+
261
+ class adrv9009_tx(dac, adrv9009_clock_common, adrv9009_core):
262
+ """ADRV9009 Transmit model."""
263
+
264
+ quick_configuration_modes = {"jesd204b": quick_configuration_modes_tx}
265
+ name = "ADRV9009_TX"
266
+ converter_type = "dac"
267
+
268
+ # JESD configurations
269
+ K_available = [*np.arange(1, 32 + 1)]
270
+ L_available = [1, 2, 4]
271
+ M_available = [1, 2, 4]
272
+ N_available = [12, 16]
273
+ Np_available = [12, 16]
274
+ F_available = [1, 2, 3, 4, 8]
275
+ CS_available = [0]
276
+ CF_available = [0]
277
+ S_available = [1]
278
+
279
+ # Clock constraints
280
+ bit_clock_min_available = {"jesd204b": 2457.6e6}
281
+ bit_clock_max_available = {"jesd204b": 12.288e9}
282
+
283
+ _interpolation = 8
284
+ interpolation_available = [1, 2, 4, 5, 8, 10, 16, 20, 32]
285
+ """
286
+ ADRV9009 Tx interpolation stages.
287
+
288
+ .. code-block:: none
289
+
290
+ +------------+
291
+ +--------------------+ Int 5 (5) +--------------------+
292
+ | +------------+ |
293
+ | |
294
+ | +------------+ +------------+ +------------+ | +--------------+
295
+ <---+---+ THB3 (1,2) +---+ THB2 (1,2) +---+ THB1 (1,2) +---+---+ TFIR (1,2,4) +
296
+ +------------+ +------------+ +------------+ +--------------+
297
+
298
+ """
299
+
300
+ def __init__(self, *args: Any, **kwargs: Any) -> None: # noqa: ANN401
301
+ """Initialize ADRV9009-TX class.
302
+
303
+ Objects will default to mode and sample_clock.
304
+
305
+ Args:
306
+ *args (Any): Pass through arguments
307
+ **kwargs (Any): Pass through keyword arguments
308
+ """
309
+ # Set default mode
310
+ self.set_quick_configuration_mode(str(6))
311
+ self.sample_clock = int(122.88e6)
312
+ super().__init__(*args, **kwargs)
313
+ # self._init_diagram()
314
+
315
+ def get_required_clock_names(self) -> List[str]:
316
+ """Get list of strings of names of requested clocks.
317
+
318
+ This list of names is for the clocks defined by
319
+ get_required_clocks
320
+
321
+ Returns:
322
+ List[str]: List of strings of clock names mapped by get_required_clocks
323
+ """
324
+ return ["adrv9009_tx_ref_clk", "adrv9009_tx_sysref"]
325
+
326
+
327
+ class adrv9009(adrv9009_core):
328
+ """ADRV9009 combined transmit and receive model."""
329
+
330
+ name = "ADRV9009"
331
+ solver = "CPLEX"
332
+ _nested = ["adc", "dac"]
333
+ converter_type = "adc_dac"
334
+
335
+ def __init__(
336
+ self, model: Union[GEKKO, CpoModel] = None, solver: str = None
337
+ ) -> None:
338
+ """Initialize ADRV9009 clocking model for TX and RX.
339
+
340
+ This is a common class used to handle TX and RX constraints
341
+ together.
342
+
343
+ Args:
344
+ model (GEKKO,CpoModel): Solver model
345
+ solver (str): Solver name (gekko or CPLEX)
346
+ """
347
+ if solver:
348
+ self.solver = solver
349
+ self.adc = adrv9009_rx(model, solver=self.solver)
350
+ self.dac = adrv9009_tx(model, solver=self.solver)
351
+ self.model = model
352
+
353
+ def validate_config(self) -> None:
354
+ """Validate device configurations including JESD and clocks of both ADC and DAC.
355
+
356
+ This check only is for static configuration that does not include
357
+ variables which are solved.
358
+ """
359
+ self.adc.validate_config()
360
+ self.dac.validate_config()
361
+
362
+ def _get_converters(self) -> List[Union[converter, converter]]:
363
+ return [self.adc, self.dac]
364
+
365
+ def get_required_clocks(self) -> List[Dict]:
366
+ """Generate list of required clocks.
367
+
368
+ For ADRV9009 this will contain:
369
+ [device clock requirement SOS, sysref requirement SOS]
370
+
371
+ Returns:
372
+ list[dict]: List of dictionaries of solver variables, equations, and constants
373
+
374
+ Raises:
375
+ Exception: Invalid relation of rates between RX and TX
376
+ AssertionError: Gekko called
377
+ """
378
+ # Validate sample rates feasible
379
+ if self.dac.sample_clock / self.adc.sample_clock not in [
380
+ 1,
381
+ 2,
382
+ 4,
383
+ ] or self.adc.sample_clock / self.dac.sample_clock not in [1, 2, 4]:
384
+ raise Exception(
385
+ "ADRV9009 RX and TX sample rates must be related by power of 2"
386
+ )
387
+
388
+ if self.solver == "gekko":
389
+ raise AssertionError
390
+
391
+ self.config = {}
392
+ self.config["adc_lmfc_divisor_sysref"] = self._convert_input(
393
+ self._lmfc_divisor_sysref_available, name="adc_lmfc_divisor_sysref"
394
+ )
395
+ self.config["dac_lmfc_divisor_sysref"] = self._convert_input(
396
+ self._lmfc_divisor_sysref_available, name="dac_lmfc_divisor_sysref"
397
+ )
398
+
399
+ self.config["input_clock_divider_x2"] = self._convert_input(
400
+ self.input_clock_dividers_times2_available
401
+ )
402
+
403
+ faster_clk = max([self.adc.sample_clock, self.dac.sample_clock])
404
+ self.config["ref_clk"] = self._add_intermediate(
405
+ faster_clk / self.config["input_clock_divider_x2"]
406
+ )
407
+
408
+ self.config["sysref_adc"] = self._add_intermediate(
409
+ self.adc.multiframe_clock / self.config["adc_lmfc_divisor_sysref"]
410
+ )
411
+ self.config["sysref_dac"] = self._add_intermediate(
412
+ self.dac.multiframe_clock / self.config["dac_lmfc_divisor_sysref"]
413
+ )
414
+
415
+ self._add_equation(
416
+ [
417
+ self.device_clock_min <= self.config["ref_clk"],
418
+ self.config["ref_clk"] <= self.device_clock_max,
419
+ ]
420
+ )
421
+
422
+ return [
423
+ self.config["ref_clk"],
424
+ self.config["sysref_adc"],
425
+ self.config["sysref_dac"],
426
+ ]