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
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"""AD9144 high speed DAC clocking model."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Union
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from adijif.converters.ad9144_bf import ad9144_bf
|
|
8
|
+
|
|
9
|
+
from ..solvers import CpoSolveResult # noqa: I202
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _convert_to_config(
|
|
13
|
+
L: Union[int, float],
|
|
14
|
+
M: Union[int, float],
|
|
15
|
+
F: Union[int, float],
|
|
16
|
+
S: Union[int, float],
|
|
17
|
+
# HD: Union[int, float],
|
|
18
|
+
N: Union[int, float],
|
|
19
|
+
Np: Union[int, float],
|
|
20
|
+
# CS: Union[int, float],
|
|
21
|
+
DualLink: bool,
|
|
22
|
+
) -> Dict:
|
|
23
|
+
# return {"L": L, "M": M, "F": F, "S": S, "HD": HD, "N": N, "Np": Np, "CS": CS}
|
|
24
|
+
return {
|
|
25
|
+
"L": L,
|
|
26
|
+
"M": M,
|
|
27
|
+
"F": F,
|
|
28
|
+
"S": S,
|
|
29
|
+
"HD": 1 if F == 1 else 0,
|
|
30
|
+
"Np": Np,
|
|
31
|
+
"CS": 0,
|
|
32
|
+
"DualLink": DualLink,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# IF F==1, HD=1, otherwise 0
|
|
37
|
+
quick_configuration_modes = {
|
|
38
|
+
str(0): _convert_to_config(DualLink=False, M=4, L=8, S=1, F=1, N=16, Np=16),
|
|
39
|
+
str(1): _convert_to_config(DualLink=False, M=4, L=8, S=2, F=2, N=16, Np=16),
|
|
40
|
+
str(2): _convert_to_config(DualLink=False, M=4, L=4, S=1, F=2, N=16, Np=16),
|
|
41
|
+
str(3): _convert_to_config(DualLink=False, M=4, L=2, S=1, F=4, N=16, Np=16),
|
|
42
|
+
str(4): _convert_to_config(DualLink=False, M=2, L=4, S=1, F=1, N=16, Np=16),
|
|
43
|
+
str(5): _convert_to_config(DualLink=False, M=2, L=4, S=2, F=2, N=16, Np=16),
|
|
44
|
+
str(6): _convert_to_config(DualLink=False, M=2, L=2, S=1, F=2, N=16, Np=16),
|
|
45
|
+
str(7): _convert_to_config(DualLink=False, M=2, L=1, S=1, F=4, N=16, Np=16),
|
|
46
|
+
# 8 is missing in datasheet
|
|
47
|
+
str(9): _convert_to_config(DualLink=False, M=1, L=2, S=1, F=1, N=16, Np=16),
|
|
48
|
+
str(10): _convert_to_config(DualLink=False, M=1, L=1, S=1, F=2, N=16, Np=16),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
quick_configuration_modes = {
|
|
52
|
+
**quick_configuration_modes,
|
|
53
|
+
**{
|
|
54
|
+
str(4)
|
|
55
|
+
+ "-DL": _convert_to_config(DualLink=False, M=2, L=4, S=1, F=1, N=16, Np=16),
|
|
56
|
+
str(5)
|
|
57
|
+
+ "-DL": _convert_to_config(DualLink=False, M=2, L=4, S=2, F=2, N=16, Np=16),
|
|
58
|
+
str(6)
|
|
59
|
+
+ "-DL": _convert_to_config(DualLink=False, M=2, L=2, S=1, F=2, N=16, Np=16),
|
|
60
|
+
str(7)
|
|
61
|
+
+ "-DL": _convert_to_config(DualLink=False, M=2, L=1, S=1, F=4, N=16, Np=16),
|
|
62
|
+
# 8 is missing in datasheet
|
|
63
|
+
str(9)
|
|
64
|
+
+ "-DL": _convert_to_config(DualLink=False, M=1, L=2, S=1, F=1, N=16, Np=16),
|
|
65
|
+
str(10)
|
|
66
|
+
+ "-DL": _convert_to_config(DualLink=False, M=1, L=1, S=1, F=2, N=16, Np=16),
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ad9144(ad9144_bf):
|
|
72
|
+
"""AD9144 high speed DAC model.
|
|
73
|
+
|
|
74
|
+
This model supports both direct clock configurations and on-board
|
|
75
|
+
generation
|
|
76
|
+
|
|
77
|
+
Clocking: AD9144 has directly clocked DAC that have optional input dividers.
|
|
78
|
+
The sample rate can be determined as follows:
|
|
79
|
+
|
|
80
|
+
baseband_sample_rate = (input_clock / input_clock_divider) / datapath_decimation
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
name = "AD9144"
|
|
84
|
+
converter_type = "DAC"
|
|
85
|
+
|
|
86
|
+
# JESD parameters
|
|
87
|
+
_jesd_params_to_skip_check = ["DualLink", "K"]
|
|
88
|
+
DualLink = False
|
|
89
|
+
available_jesd_modes = ["jesd204b"]
|
|
90
|
+
K_available = [16, 32]
|
|
91
|
+
_K = 32 # Valid for all cases pg 31 of datasheet
|
|
92
|
+
L_available = [1, 2, 4, 8]
|
|
93
|
+
M_available = [1, 2, 4, 4]
|
|
94
|
+
N_available = [*range(7, 16 + 1)]
|
|
95
|
+
Np_available = [8, 16]
|
|
96
|
+
F_available = [1, 2, 4, 8, 16]
|
|
97
|
+
CS_available = [0, 1, 2, 3]
|
|
98
|
+
CF_available = [0]
|
|
99
|
+
S_available = [1, 2]
|
|
100
|
+
|
|
101
|
+
# Clock constraints
|
|
102
|
+
clocking_option_available = ["integrated_pll", "direct"]
|
|
103
|
+
_clocking_option = "direct"
|
|
104
|
+
|
|
105
|
+
converter_clock_min = 420e6 # checked by dac
|
|
106
|
+
converter_clock_max = 2.8e9 # checked by dac
|
|
107
|
+
|
|
108
|
+
bit_clock_min_available = {"jesd204b": 1.44e9} # checked by jesd
|
|
109
|
+
bit_clock_max_available = {"jesd204b": 12.4e9} # checked by jesd
|
|
110
|
+
|
|
111
|
+
sample_clock_min = 1.44e9 / 40 # checked by jesd
|
|
112
|
+
sample_clock_max = 1.06e9 # checked by jesd
|
|
113
|
+
|
|
114
|
+
interpolation_available = [1, 2, 4, 8]
|
|
115
|
+
_interpolation = 1
|
|
116
|
+
|
|
117
|
+
quick_configuration_modes = {"jesd204b": quick_configuration_modes}
|
|
118
|
+
|
|
119
|
+
# Input clock requirements
|
|
120
|
+
input_clock_divider_available = [
|
|
121
|
+
1,
|
|
122
|
+
2,
|
|
123
|
+
4,
|
|
124
|
+
8,
|
|
125
|
+
16,
|
|
126
|
+
] # only used with integrated PLL for now
|
|
127
|
+
|
|
128
|
+
# Integrated PLL limits
|
|
129
|
+
pfd_min = 35e6
|
|
130
|
+
pfd_max = 80e6
|
|
131
|
+
|
|
132
|
+
# With integrated PLL
|
|
133
|
+
input_clock_min = 35e6
|
|
134
|
+
input_clock_max = 1e9
|
|
135
|
+
|
|
136
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None: # noqa: ANN401
|
|
137
|
+
"""Initialize AD9144 class.
|
|
138
|
+
|
|
139
|
+
Objects will default to mode 0x88 with 1e9 sample_clock.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
*args (Any): Pass through arguments
|
|
143
|
+
**kwargs (Any): Pass through keyword arguments
|
|
144
|
+
"""
|
|
145
|
+
# Set default mode
|
|
146
|
+
self.set_quick_configuration_mode(str(9))
|
|
147
|
+
self.sample_clock = 1e9
|
|
148
|
+
super().__init__(*args, **kwargs)
|
|
149
|
+
# self._init_diagram()
|
|
150
|
+
|
|
151
|
+
def get_config(self, solution: CpoSolveResult = None) -> Dict:
|
|
152
|
+
"""Extract configurations from solver results.
|
|
153
|
+
|
|
154
|
+
Collect internal converter configuration and output clock definitions
|
|
155
|
+
leading to connected devices (clock chips, FPGAs)
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
solution (CpoSolveResult): CPlex solution. Only needed for CPlex solver
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Dict: Dictionary of clocking rates and dividers for configuration
|
|
162
|
+
"""
|
|
163
|
+
config: Dict = {
|
|
164
|
+
"clocking_option": self.clocking_option,
|
|
165
|
+
"interpolation": self._interpolation,
|
|
166
|
+
}
|
|
167
|
+
if self.clocking_option == "direct":
|
|
168
|
+
return config
|
|
169
|
+
|
|
170
|
+
if self.solver == "CPLEX":
|
|
171
|
+
if solution:
|
|
172
|
+
self.solution = solution
|
|
173
|
+
config.update(
|
|
174
|
+
{
|
|
175
|
+
"BCount": self._get_val(self.config["BCount"]),
|
|
176
|
+
"ref_div_factor": self._get_val(self.config["ref_div_factor"]),
|
|
177
|
+
"lo_div_mode": np.log2(
|
|
178
|
+
self._get_val(self.config["lo_div_mode_p2"])
|
|
179
|
+
),
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
return config
|
|
183
|
+
|
|
184
|
+
def get_required_clock_names(self) -> List[str]:
|
|
185
|
+
"""Get list of strings of names of requested clocks.
|
|
186
|
+
|
|
187
|
+
This list of names is for the clocks defined by get_required_clocks
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
List[str]: List of strings of clock names in order
|
|
191
|
+
"""
|
|
192
|
+
# clk = (
|
|
193
|
+
# "AD9144_dac_clock" if self.clocking_option == "direct" else "AD9144_pll_ref"
|
|
194
|
+
# )
|
|
195
|
+
clk = "ad9144_ref_clk"
|
|
196
|
+
return [clk, "ad9144_sysref"]
|
|
197
|
+
|
|
198
|
+
def _check_valid_internal_configuration(self) -> None:
|
|
199
|
+
mode = self._check_valid_jesd_mode()
|
|
200
|
+
# cfg = self.quick_configuration_modes[self.jesd_class][mode]
|
|
201
|
+
|
|
202
|
+
if mode in ["0", "4", "9"]:
|
|
203
|
+
assert self.K == 32, "K must be 32 for JESD mode 0, 4, or 9"
|
|
204
|
+
|
|
205
|
+
def _pll_config(self) -> Dict:
|
|
206
|
+
dac_clk = self.interpolation * self.sample_clock
|
|
207
|
+
self.config["dac_clk"] = self._convert_input(dac_clk, "dac_clk")
|
|
208
|
+
|
|
209
|
+
self.config["BCount"] = self._convert_input([*range(6, 127 + 1)], name="BCount")
|
|
210
|
+
|
|
211
|
+
# Datasheet says refclk div can support 32 but tables do not reflect this and
|
|
212
|
+
# a div of 32 would put you under supported range
|
|
213
|
+
|
|
214
|
+
if self.solver == "gekko":
|
|
215
|
+
self.config["ref_div_factor"] = self.model.sos1(
|
|
216
|
+
self.input_clock_divider_available
|
|
217
|
+
)
|
|
218
|
+
# self.config["ref_div_factor_i"] = self.model.Var(
|
|
219
|
+
# integer=True, lb=0, ub=4, value=4
|
|
220
|
+
# )
|
|
221
|
+
# self.config["ref_div_factor"] = self.model.Intermediate(
|
|
222
|
+
# 2 ** (self.config["ref_div_factor_i"])
|
|
223
|
+
# )
|
|
224
|
+
|
|
225
|
+
self.config["ref_clk"] = self.model.Var(
|
|
226
|
+
integer=False, lb=35e6, ub=1e9, value=35e6
|
|
227
|
+
)
|
|
228
|
+
elif self.solver == "CPLEX":
|
|
229
|
+
self.config["ref_div_factor"] = self._convert_input(
|
|
230
|
+
self.input_clock_divider_available, "ref_div_factor"
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
self.config["ref_clk"] = (
|
|
234
|
+
self.config["dac_clk"]
|
|
235
|
+
* self.config["ref_div_factor"]
|
|
236
|
+
/ self.config["BCount"]
|
|
237
|
+
/ 2
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
if dac_clk > 2800e6:
|
|
241
|
+
raise Exception("DAC Clock too fast")
|
|
242
|
+
elif dac_clk >= 1500e6:
|
|
243
|
+
self.config["lo_div_mode_p2"] = self._convert_input(
|
|
244
|
+
2 ** (1 + 1), name="lo_div_mode_p2"
|
|
245
|
+
)
|
|
246
|
+
elif dac_clk >= 720e6:
|
|
247
|
+
self.config["lo_div_mode_p2"] = self._convert_input(
|
|
248
|
+
2 ** (2 + 1), name="lo_div_mode_p2"
|
|
249
|
+
)
|
|
250
|
+
elif dac_clk >= 420e6:
|
|
251
|
+
self.config["lo_div_mode_p2"] = self._convert_input(
|
|
252
|
+
2 ** (3 + 1), name="lo_div_mode_p2"
|
|
253
|
+
)
|
|
254
|
+
else:
|
|
255
|
+
raise Exception("DAC Clock too slow")
|
|
256
|
+
|
|
257
|
+
self.config["vco"] = self._add_intermediate(
|
|
258
|
+
self.config["dac_clk"] * self.config["lo_div_mode_p2"]
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
self._add_equation(
|
|
262
|
+
[
|
|
263
|
+
self.config["ref_div_factor"] * self.pfd_min < self.config["ref_clk"],
|
|
264
|
+
self.config["ref_div_factor"] * self.pfd_max > self.config["ref_clk"],
|
|
265
|
+
self.config["ref_clk"] >= self.input_clock_min,
|
|
266
|
+
self.config["ref_clk"] <= self.input_clock_max,
|
|
267
|
+
]
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
if self.solver == "gekko":
|
|
271
|
+
self._add_equation(
|
|
272
|
+
[
|
|
273
|
+
self.config["ref_clk"] * 2 * self.config["BCount"]
|
|
274
|
+
== self.config["dac_clk"] * self.config["ref_div_factor"]
|
|
275
|
+
]
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return self.config["ref_clk"]
|
|
279
|
+
|
|
280
|
+
def get_required_clocks(self) -> List:
|
|
281
|
+
"""Generate list required clocks.
|
|
282
|
+
|
|
283
|
+
For AD9144 this will contain [converter clock, sysref requirement SOS]
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
List: List of dictionaries of solver components
|
|
287
|
+
"""
|
|
288
|
+
self.config = {}
|
|
289
|
+
|
|
290
|
+
self.config["lmfc_divisor_sysref"] = self._convert_input(
|
|
291
|
+
[*range(1, 20 + 1)], name="lmfc_divisor_sysref"
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
self.config["sysref"] = self._add_intermediate(
|
|
295
|
+
self.multiframe_clock / (self.config["lmfc_divisor_sysref"])
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
if self.clocking_option == "direct":
|
|
299
|
+
clk = self.sample_clock * self.interpolation
|
|
300
|
+
# LaneRate = (20 × DataRate × M)/L
|
|
301
|
+
assert (
|
|
302
|
+
self.bit_clock == (20 * self.sample_clock * self.M) / self.L
|
|
303
|
+
), "AD9144 direct clock requirement invalid"
|
|
304
|
+
else:
|
|
305
|
+
# vco = dac_clk * 2^(LO_DIV_MODE + 1)
|
|
306
|
+
# 6 GHz <= vco <= 12 GHz
|
|
307
|
+
# BCount = floor( dac_clk/(2 * ref_clk/ref_div ) )
|
|
308
|
+
# 5 <= BCount <= 127
|
|
309
|
+
# ref_div = 2^ref_div_mode = 1,2,4,8,16
|
|
310
|
+
clk = self._pll_config() # type: ignore
|
|
311
|
+
|
|
312
|
+
# Objectives
|
|
313
|
+
# self.model.Obj(self.config["sysref"]) # This breaks many searches
|
|
314
|
+
# self.model.Obj(-1*self.config["lmfc_divisor_sysref"])
|
|
315
|
+
|
|
316
|
+
return [clk, self.config["sysref"]]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from adijif.converters.dac import dac
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ad9144_bf(dac):
|
|
7
|
+
"""Brute force methods for calculating clocks
|
|
8
|
+
|
|
9
|
+
These are currently meant for debug to compare against
|
|
10
|
+
the solver solutions
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def device_clock_available(self):
|
|
14
|
+
"""Generate list of possible device clocks"""
|
|
15
|
+
raise Exception("Not implemented")
|
|
16
|
+
# aicd = sorted(self.available_input_clock_dividers)
|
|
17
|
+
|
|
18
|
+
# dev_clocks = []
|
|
19
|
+
# for div in aicd:
|
|
20
|
+
# in_clock = self.sample_clock * self.datapath_decimation * div
|
|
21
|
+
# if in_clock <= self.max_input_clock:
|
|
22
|
+
# dev_clocks.append(in_clock)
|
|
23
|
+
# if not dev_clocks:
|
|
24
|
+
# raise Exception(
|
|
25
|
+
# "No device clocks possible in current config. Sample rate too high"
|
|
26
|
+
# )
|
|
27
|
+
# return dev_clocks
|
|
28
|
+
|
|
29
|
+
def device_clock_ranges(self):
|
|
30
|
+
"""Generate min and max values for device clock"""
|
|
31
|
+
|
|
32
|
+
clks = self.device_clock_available()
|
|
33
|
+
return np.min(clks), np.max(clks)
|
|
34
|
+
|
|
35
|
+
def sysref_clock_ranges(self):
|
|
36
|
+
"""sysref must be multiple of LMFC"""
|
|
37
|
+
lmfc = self.multiframe_clock
|
|
38
|
+
return lmfc / 2048, lmfc / 2
|
|
39
|
+
|
|
40
|
+
def sysref_met(self, sysref_clock, sample_clock):
|
|
41
|
+
if sysref_clock % self.multiframe_clock != 0:
|
|
42
|
+
raise Exception("SYSREF not a multiple of LMFC")
|
|
43
|
+
if (self.multiframe_clock / sysref_clock) < 2 * self.input_clock_divider:
|
|
44
|
+
raise Exception("SYSREF not a multiple of LMFC > 1")
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""AD9680 high speed ADC clocking model."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Union
|
|
4
|
+
|
|
5
|
+
from adijif.converters.ad9680_bf import ad9680_bf
|
|
6
|
+
|
|
7
|
+
from ..solvers import CpoSolveResult # noqa: I202
|
|
8
|
+
from .ad9680_draw import ad9680_draw # noqa: I202
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _convert_to_config(
|
|
12
|
+
L: Union[int, float],
|
|
13
|
+
M: Union[int, float],
|
|
14
|
+
F: Union[int, float],
|
|
15
|
+
S: Union[int, float],
|
|
16
|
+
HD: Union[int, float],
|
|
17
|
+
N: Union[int, float],
|
|
18
|
+
Np: Union[int, float],
|
|
19
|
+
CS: Union[int, float],
|
|
20
|
+
) -> Dict:
|
|
21
|
+
# return {"L": L, "M": M, "F": F, "S": S, "HD": HD, "N": N, "Np": Np, "CS": CS}
|
|
22
|
+
return {
|
|
23
|
+
"L": L,
|
|
24
|
+
"M": M,
|
|
25
|
+
"F": F,
|
|
26
|
+
"S": S,
|
|
27
|
+
"HD": HD,
|
|
28
|
+
"Np": Np,
|
|
29
|
+
"jesd_class": "jesd204b",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
quick_configuration_modes = {
|
|
34
|
+
# M = 1
|
|
35
|
+
str(0x01): _convert_to_config(1, 1, 2, 1, 0, -1, 16, -1),
|
|
36
|
+
str(0x40): _convert_to_config(2, 1, 1, 1, 1, -1, 16, -1),
|
|
37
|
+
str(0x41): _convert_to_config(2, 1, 2, 2, 0, -1, 16, -1),
|
|
38
|
+
str(0x80): _convert_to_config(4, 1, 1, 2, 1, -1, 16, -1),
|
|
39
|
+
str(0x81): _convert_to_config(4, 1, 2, 4, 0, -1, 16, -1),
|
|
40
|
+
# M = 2
|
|
41
|
+
str(0x0A): _convert_to_config(1, 2, 4, 1, 0, -1, 16, -1),
|
|
42
|
+
str(0x49): _convert_to_config(2, 2, 2, 1, 0, -1, 16, -1),
|
|
43
|
+
str(0x88): _convert_to_config(4, 2, 1, 1, 1, -1, 16, -1),
|
|
44
|
+
str(0x89): _convert_to_config(4, 2, 2, 2, 0, -1, 16, -1),
|
|
45
|
+
# M = 4
|
|
46
|
+
str(0x13): _convert_to_config(1, 4, 8, 1, 0, -1, 16, -1),
|
|
47
|
+
str(0x52): _convert_to_config(2, 4, 4, 1, 0, -1, 16, -1),
|
|
48
|
+
str(0x91): _convert_to_config(4, 4, 2, 1, 0, -1, 16, -1),
|
|
49
|
+
# M = 8
|
|
50
|
+
str(0x1C): _convert_to_config(1, 8, 16, 1, 0, -1, 16, -1),
|
|
51
|
+
str(0x5B): _convert_to_config(2, 8, 8, 1, 0, -1, 16, -1),
|
|
52
|
+
str(0x9A): _convert_to_config(4, 8, 4, 1, 0, -1, 16, -1),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ad9680(ad9680_draw, ad9680_bf):
|
|
57
|
+
"""AD9680 high speed ADC model.
|
|
58
|
+
|
|
59
|
+
This model supports direct clock configurations
|
|
60
|
+
|
|
61
|
+
Clocking: AD9680 has directly clocked ADC that have optional input dividers.
|
|
62
|
+
The sample rate can be determined as follows:
|
|
63
|
+
|
|
64
|
+
baseband_sample_rate = (input_clock / input_clock_divider) / datapath_decimation
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
name = "AD9680"
|
|
68
|
+
converter_type = "ADC"
|
|
69
|
+
|
|
70
|
+
# JESD parameters
|
|
71
|
+
_jesd_params_to_skip_check = ["DualLink", "CS", "N", "HD"]
|
|
72
|
+
available_jesd_modes = ["jesd204b"]
|
|
73
|
+
K_available = [4, 8, 12, 16, 20, 24, 28, 32]
|
|
74
|
+
L_available = [1, 2, 4]
|
|
75
|
+
M_available = [1, 2, 4, 8]
|
|
76
|
+
N_available = [*range(7, 16)]
|
|
77
|
+
Np_available = [8, 16]
|
|
78
|
+
F_available = [1, 2, 4, 8, 16]
|
|
79
|
+
CS_available = [0, 1, 2, 3]
|
|
80
|
+
CF_available = [0]
|
|
81
|
+
S_available = [1, 2, 4]
|
|
82
|
+
|
|
83
|
+
# Clock constraints
|
|
84
|
+
clocking_option_available = ["direct"]
|
|
85
|
+
_clocking_option = "direct"
|
|
86
|
+
converter_clock_min = 300e6
|
|
87
|
+
converter_clock_max = 1.25e9
|
|
88
|
+
bit_clock_min_available = {"jesd204b": 3.125e9}
|
|
89
|
+
bit_clock_max_available = {"jesd204b": 12.5e9}
|
|
90
|
+
sample_clock_min = 300e6
|
|
91
|
+
sample_clock_max = 1250e6
|
|
92
|
+
decimation_available = [1, 2, 4, 8, 16]
|
|
93
|
+
_decimation = 1
|
|
94
|
+
_lmfc_sys_divisor = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
95
|
+
|
|
96
|
+
quick_configuration_modes = {"jesd204b": quick_configuration_modes}
|
|
97
|
+
|
|
98
|
+
# Input clock requirements
|
|
99
|
+
input_clock_divider_available = [1, 2, 4, 8] # FIXME
|
|
100
|
+
input_clock_divider = 1 # FIXME
|
|
101
|
+
input_clock_max = 4e9 # FIXME
|
|
102
|
+
|
|
103
|
+
""" Clocking
|
|
104
|
+
AD9680 has directly clocked ADCs that have optional input dividers.
|
|
105
|
+
The sample rate can be determined as follows:
|
|
106
|
+
|
|
107
|
+
baseband_sample_rate = (input_clock / input_clock_divider) / datapath_decimation
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None: # noqa: ANN401
|
|
111
|
+
"""Initialize AD9680 class.
|
|
112
|
+
|
|
113
|
+
Objects will default to mode 0x88 with 1e9 sample_clock.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
*args (Any): Pass through arguments
|
|
117
|
+
**kwargs (Any): Pass through keyword arguments
|
|
118
|
+
"""
|
|
119
|
+
# Set default mode
|
|
120
|
+
self.set_quick_configuration_mode(str(0x88))
|
|
121
|
+
self.K = 32
|
|
122
|
+
self.sample_clock = 1e9
|
|
123
|
+
super().__init__(*args, **kwargs)
|
|
124
|
+
self._init_diagram()
|
|
125
|
+
|
|
126
|
+
def _check_valid_jesd_mode(self) -> str:
|
|
127
|
+
"""Verify current JESD configuration for part is valid.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
str: Current JESD mode
|
|
131
|
+
"""
|
|
132
|
+
if self.F == 1:
|
|
133
|
+
assert self.K in [20, 24, 28, 32], "Invalid K value for F=1"
|
|
134
|
+
if self.F == 2:
|
|
135
|
+
assert self.K in [12, 16, 20, 24, 28, 32], "Invalid K value for F=1"
|
|
136
|
+
if self.F == 4:
|
|
137
|
+
assert self.K in [8, 12, 16, 20, 24, 28, 32], "Invalid K value for F=1"
|
|
138
|
+
if self.F in [8, 16]:
|
|
139
|
+
assert self.K in [4, 8, 12, 16, 20, 24, 28, 32], "Invalid K value for F=1"
|
|
140
|
+
|
|
141
|
+
return super()._check_valid_jesd_mode()
|
|
142
|
+
|
|
143
|
+
def get_config(self, solution: CpoSolveResult = None) -> Dict:
|
|
144
|
+
"""Extract configurations from solver results.
|
|
145
|
+
|
|
146
|
+
Collect internal converter configuration and output clock definitions
|
|
147
|
+
leading to connected devices (clock chips, FPGAs)
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
solution (CpoSolveResult): CPlex solution. Only needed for CPlex solver
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Dict: Dictionary of clocking rates and dividers for configuration
|
|
154
|
+
"""
|
|
155
|
+
if solution:
|
|
156
|
+
self._saved_solution = solution
|
|
157
|
+
return {"clocking_option": self.clocking_option, "decimation": self.decimation}
|
|
158
|
+
|
|
159
|
+
def get_required_clock_names(self) -> List[str]:
|
|
160
|
+
"""Get list of strings of names of requested clocks.
|
|
161
|
+
|
|
162
|
+
This list of names is for the clocks defined by get_required_clocks
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
List[str]: List of strings of clock names in order
|
|
166
|
+
"""
|
|
167
|
+
return ["AD9680_ref_clk", "AD9680_sysref"]
|
|
168
|
+
|
|
169
|
+
def get_required_clocks(self) -> List:
|
|
170
|
+
"""Generate list required clocks.
|
|
171
|
+
|
|
172
|
+
For AD9680 this will contain [converter clock, sysref requirement SOS]
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
List: List of solver variables, equations, and constants
|
|
176
|
+
"""
|
|
177
|
+
# possible_sysrefs = []
|
|
178
|
+
# for n in range(1, 10):
|
|
179
|
+
# r = self.multiframe_clock / (n * n)
|
|
180
|
+
# if r == int(r) and r > 1e6:
|
|
181
|
+
# possible_sysrefs.append(r)
|
|
182
|
+
# self.config = {"sysref": self.model.sos1(possible_sysrefs)}
|
|
183
|
+
|
|
184
|
+
self.config = {}
|
|
185
|
+
self.config["lmfc_divisor_sysref"] = self._convert_input(
|
|
186
|
+
self._lmfc_sys_divisor,
|
|
187
|
+
default=self._lmfc_sys_divisor[-1],
|
|
188
|
+
name="AD9680_lmfc_divisor_sysref",
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
self.config["lmfc_divisor_sysref_squared"] = self._add_intermediate(
|
|
192
|
+
self.config["lmfc_divisor_sysref"] * self.config["lmfc_divisor_sysref"]
|
|
193
|
+
)
|
|
194
|
+
self.config["sysref"] = self._add_intermediate(
|
|
195
|
+
self.multiframe_clock / self.config["lmfc_divisor_sysref_squared"]
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Objectives
|
|
199
|
+
# self.model.Obj(self.config["sysref"]) # This breaks many searches
|
|
200
|
+
|
|
201
|
+
return [self.converter_clock, self.config["sysref"]]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from adijif.converters.adc import adc
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ad9680_bf(adc):
|
|
7
|
+
"""Brute force methods for calculating clocks
|
|
8
|
+
|
|
9
|
+
These are currently meant for debug to compare against
|
|
10
|
+
the solver solutions
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def device_clock_available(self):
|
|
14
|
+
"""Generate list of possible device clocks"""
|
|
15
|
+
aicd = sorted(self.input_clock_divider_available)
|
|
16
|
+
|
|
17
|
+
dev_clocks = []
|
|
18
|
+
for div in aicd:
|
|
19
|
+
in_clock = self.sample_clock * self.decimation * div
|
|
20
|
+
if in_clock <= self.input_clock_max:
|
|
21
|
+
dev_clocks.append(in_clock)
|
|
22
|
+
if not dev_clocks:
|
|
23
|
+
raise Exception(
|
|
24
|
+
"No device clocks possible in current config. Sample rate too high"
|
|
25
|
+
)
|
|
26
|
+
return dev_clocks
|
|
27
|
+
|
|
28
|
+
def device_clock_ranges(self):
|
|
29
|
+
"""Generate min and max values for device clock"""
|
|
30
|
+
|
|
31
|
+
clks = self.device_clock_available()
|
|
32
|
+
return np.min(clks), np.max(clks)
|
|
33
|
+
|
|
34
|
+
def sysref_clock_ranges(self):
|
|
35
|
+
"""sysref must be multiple of LMFC"""
|
|
36
|
+
lmfc = self.multiframe_clock
|
|
37
|
+
return lmfc / 2048, lmfc / 2
|
|
38
|
+
|
|
39
|
+
def sysref_met(self, sysref_clock, sample_clock):
|
|
40
|
+
if sysref_clock % self.multiframe_clock != 0:
|
|
41
|
+
raise Exception("SYSREF not a multiple of LMFC")
|
|
42
|
+
if (self.multiframe_clock / sysref_clock) < 2 * self.input_clock_divider:
|
|
43
|
+
raise Exception("SYSREF not a multiple of LMFC > 1")
|