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,203 @@
1
+ """Drawing features for AD9084."""
2
+
3
+ from typing import Dict
4
+
5
+ from adijif.draw import Layout, Node # type: ignore # isort: skip # noqa: I202
6
+
7
+
8
+ class ad9084_draw:
9
+ """AD9084 drawing features."""
10
+
11
+ _system_draw = False
12
+ show_rates = True
13
+
14
+ def _init_diagram(self) -> None:
15
+ """Initialize diagram for AD9084 alone."""
16
+ self.ic_diagram_node = None
17
+ self._diagram_output_dividers = []
18
+
19
+ self.ic_diagram_node = Node("AD9084")
20
+
21
+ # External
22
+ # ref_in = Node("REF_IN", ntype="input")
23
+ # lo.add_node(ref_in)
24
+
25
+ crossbar = Node("MUX0", ntype="crossbar")
26
+ crossbar_rm = Node("Router MUX", ntype="crossbar")
27
+
28
+ self.ic_diagram_node.add_child(crossbar)
29
+ self.ic_diagram_node.add_child(crossbar_rm)
30
+
31
+ for adc in range(4):
32
+ adc_node = Node(f"ADC{adc}", ntype="adc")
33
+ self.ic_diagram_node.add_child(adc_node)
34
+ adc_node.shape = "parallelogram"
35
+ self.ic_diagram_node.add_connection({"from": adc_node, "to": crossbar})
36
+
37
+ for cddc in range(4):
38
+ cddc_node = Node(f"CDDC{cddc}", ntype="ddc")
39
+ self.ic_diagram_node.add_child(cddc_node)
40
+ self.ic_diagram_node.add_connection({"from": crossbar, "to": cddc_node})
41
+ self.ic_diagram_node.add_connection({"from": cddc_node, "to": crossbar_rm})
42
+
43
+ for fddc in range(8):
44
+ fddc_node = Node(f"FDDC{fddc}", ntype="ddc")
45
+ self.ic_diagram_node.add_child(fddc_node)
46
+ self.ic_diagram_node.add_connection({"from": crossbar_rm, "to": fddc_node})
47
+
48
+ jesd204_framer = Node("JESD204 Framer", ntype="jesd204framer")
49
+ self.ic_diagram_node.add_child(jesd204_framer)
50
+
51
+ for ddc in range(8):
52
+ fddc = self.ic_diagram_node.get_child(f"FDDC{ddc}")
53
+ self.ic_diagram_node.add_connection({"from": fddc, "to": jesd204_framer})
54
+
55
+ def _update_diagram(self, config: Dict) -> None:
56
+ """Update diagram with configuration.
57
+
58
+ Args:
59
+ config (Dict): Configuration dictionary
60
+
61
+ Raises:
62
+ Exception: If key is not D followed by a number
63
+ """
64
+ # Add output dividers
65
+ keys = config.keys()
66
+ output_dividers = self.ic_diagram_node.get_child("Output Dividers")
67
+ for key in keys:
68
+ if key.startswith("D"):
69
+ div = Node(key, ntype="divider")
70
+ output_dividers.add_child(div)
71
+ self.ic_diagram_node.add_connection(
72
+ {"from": output_dividers, "to": div}
73
+ )
74
+ else:
75
+ raise Exception(
76
+ f"Unknown key {key}. Must be of for DX where X is a number"
77
+ )
78
+
79
+ def draw(
80
+ self, clocks: Dict, lo: Layout = None, clock_chip_node: Node = None
81
+ ) -> str:
82
+ """Draw diagram in d2 language for IC alone with reference clock.
83
+
84
+ Args:
85
+ clocks (Dict): Dictionary of clocks
86
+ lo (Layout): Layout object to add to. Defaults to None.
87
+ clock_chip_node (Node): Node to connect to. Defaults to None.
88
+
89
+ Returns:
90
+ str: Diagram in d2 language
91
+
92
+ Raises:
93
+ Exception: If no solution is saved
94
+ """
95
+ if not self._saved_solution:
96
+ raise Exception("No solution to draw. Must call solve first.")
97
+
98
+ system_draw = lo is not None
99
+
100
+ if not system_draw:
101
+ lo = Layout("AD9084 Example")
102
+ lo.show_rates = self.show_rates
103
+ else:
104
+ # Verify lo is a Layout object
105
+ assert isinstance(lo, Layout), "lo must be a Layout object"
106
+ lo.add_node(self.ic_diagram_node)
107
+
108
+ if not system_draw:
109
+ ref_in = Node("REF_IN", ntype="input")
110
+ lo.add_node(ref_in)
111
+ else:
112
+ to_node = lo.get_node("AD9084_ref_clk")
113
+ # Locate node connected to this one
114
+ from_node = lo.get_connection(to=to_node.name)
115
+ assert from_node, "No connection found"
116
+ assert isinstance(from_node, list), "Connection must be a list"
117
+ assert len(from_node) == 1, "Only one connection allowed"
118
+ ref_in = from_node[0]["from"]
119
+ # Remove to_node since it is not needed
120
+ lo.remove_node(to_node.name)
121
+
122
+ for i in range(4):
123
+ adc = self.ic_diagram_node.get_child(f"ADC{i}")
124
+ lo.add_connection(
125
+ {"from": ref_in, "to": adc, "rate": clocks["AD9084_ref_clk"]}
126
+ )
127
+
128
+ # Update Node values
129
+ fddc_index = 0
130
+ for cddc in range(4):
131
+ rate = clocks["AD9084_ref_clk"]
132
+ self.ic_diagram_node.update_connection("MUX0", f"CDDC{cddc}", rate)
133
+
134
+ cddc_node = self.ic_diagram_node.get_child(f"CDDC{cddc}")
135
+ cddc_node.value = str(self.datapath.cddc_decimations[cddc])
136
+ drate = rate / self.datapath.cddc_decimations[cddc]
137
+
138
+ self.ic_diagram_node.update_connection(f"CDDC{cddc}", "Router MUX", drate)
139
+
140
+ self.ic_diagram_node.update_connection(
141
+ "Router MUX", f"FDDC{fddc_index}", drate
142
+ )
143
+ fddc_rate_out = drate / self.datapath.fddc_decimations[fddc_index]
144
+ self.ic_diagram_node.update_connection(
145
+ f"FDDC{fddc_index}", "JESD204 Framer", fddc_rate_out
146
+ )
147
+ fddc_index += 1
148
+
149
+ self.ic_diagram_node.update_connection(
150
+ "Router MUX", f"FDDC{fddc_index}", drate
151
+ )
152
+ fddc_rate_out = drate / self.datapath.fddc_decimations[fddc_index]
153
+ self.ic_diagram_node.update_connection(
154
+ f"FDDC{fddc_index}", "JESD204 Framer", fddc_rate_out
155
+ )
156
+ fddc_index += 1
157
+
158
+ # Connect clock to framer
159
+ if not system_draw:
160
+ sysref_in = Node("SYSREF_IN", ntype="input")
161
+
162
+ lo.add_connection(
163
+ {
164
+ "from": sysref_in,
165
+ "to": self.ic_diagram_node.get_child("JESD204 Framer"),
166
+ "rate": clocks["AD9084_sysref"],
167
+ }
168
+ )
169
+ else:
170
+ to_node = lo.get_node("AD9084_sysref")
171
+ # Locate node connected to this one
172
+ from_node = lo.get_connection(to=to_node.name)
173
+ assert from_node, "No connection found"
174
+ assert isinstance(from_node, list), "Connection must be a list"
175
+ assert len(from_node) == 1, "Only one connection allowed"
176
+ sysref_in = from_node[0]["from"]
177
+ # Remove to_node since it is not needed
178
+ lo.remove_node(to_node.name)
179
+
180
+ lo.add_connection(
181
+ {
182
+ "from": sysref_in,
183
+ "to": self.ic_diagram_node.get_child("JESD204 Framer"),
184
+ "rate": clocks["AD9084_sysref"],
185
+ }
186
+ )
187
+
188
+ # Connect Remote Deframer
189
+ remote_deframer = Node("JESD204 Deframer", ntype="deframer")
190
+
191
+ # Add connect for each lane
192
+ for _ in range(self.L):
193
+ lane_rate = self.bit_clock
194
+ lo.add_connection(
195
+ {
196
+ "from": self.ic_diagram_node.get_child("JESD204 Framer"),
197
+ "to": remote_deframer,
198
+ "rate": lane_rate,
199
+ }
200
+ )
201
+
202
+ if not system_draw:
203
+ return lo.draw()
@@ -0,0 +1,365 @@
1
+ """AD9084 MxFE Utility Functions."""
2
+
3
+ import json
4
+ import os
5
+ from typing import Dict, Union
6
+
7
+ import pandas as pd
8
+
9
+ from ..utils import get_jesd_mode_from_params
10
+ from .converter import converter
11
+
12
+
13
+ def _convert_to_config(
14
+ mode: str,
15
+ L: Union[int, float],
16
+ M: Union[int, float],
17
+ F: Union[int, float],
18
+ S: Union[int, float],
19
+ HD: Union[int, float],
20
+ K: Union[int, float],
21
+ N: Union[int, float],
22
+ Np: Union[int, float],
23
+ CS: Union[int, float],
24
+ E: Union[int, float],
25
+ global_index: Union[int, float],
26
+ jesd_class: str,
27
+ ) -> Dict:
28
+ return {
29
+ "L": L,
30
+ "M": M,
31
+ "F": F,
32
+ "S": S,
33
+ # "HD": 1 if F == 1 else 0,
34
+ "Np": Np,
35
+ "K": K,
36
+ "HD": HD,
37
+ "CS": CS,
38
+ "E": E,
39
+ "jesd_class": jesd_class,
40
+ "global_index": global_index,
41
+ }
42
+
43
+
44
+ def _load_rx_config_modes() -> Dict:
45
+ """Load RX JESD configuration tables from file."""
46
+ return _read_table_xlsx("AD9084_JTX_JRX.xlsx")
47
+
48
+
49
+ def _read_table_xlsx(filename: str) -> Dict:
50
+ loc = os.path.dirname(__file__)
51
+ fn = os.path.join(loc, "resources", filename)
52
+ table = pd.read_excel(open(fn, "rb"), sheet_name="JTX_RxPath")
53
+
54
+ # strip out unique JESD modes
55
+ # table = table.drop_duplicates(subset=["JTX_MODE NUMBER"])
56
+ jrx_modes_204b = {}
57
+ jrx_modes_204c = {}
58
+ for prow in table.iterrows():
59
+ row = prow[1].to_dict()
60
+ # print(row)
61
+ jrx_modes_204c[str(row["Mode"])] = {
62
+ "L": row["L"],
63
+ "M": row["M"],
64
+ "F": row["F"],
65
+ "S": row["S"],
66
+ "HD": 1,
67
+ # 'K': row['K'],
68
+ "Np": row["Np"],
69
+ "DL": row["DL"],
70
+ "jesd_class": "jesd204c",
71
+ }
72
+
73
+ # Copy settings to 204b
74
+ for mode, config in jrx_modes_204c.items():
75
+ jrx_modes_204b[mode] = config.copy()
76
+ jrx_modes_204b[mode]["jesd_class"] = "jesd204b"
77
+ # jrx_modes_204b[mode]["HD"] = 0 # HD is always 0 for 204b
78
+
79
+ return {"jesd204b": jrx_modes_204b, "jesd204c": jrx_modes_204c}
80
+
81
+
82
+ def parse_json_config(profile_json: str, bypass_version_check: bool = False) -> Dict:
83
+ """Parse Apollo profiles and extract desired part information.
84
+
85
+ Args:
86
+ profile_json (str): Path to the profile JSON file.
87
+ bypass_version_check (bool): If True, bypasses the version check for
88
+ the profile.
89
+
90
+ Returns:
91
+ Dict: A dictionary containing parsed configuration data.
92
+
93
+ Raises:
94
+ FileNotFoundError: If the summary or profile JSON file does not exist.
95
+ KeyError: If required keys are missing in the JSON data.
96
+ Exception: If the profile is not supported or if it is a JESD204B profile.
97
+ """
98
+ use_summary = False # cannot use summary for now as its broken in ACE
99
+ summary_json = None # Needed for lint
100
+ if use_summary:
101
+ if not os.path.exists(summary_json):
102
+ raise FileNotFoundError(f"Summary JSON file does not exist: {summary_json}")
103
+ if not os.path.exists(profile_json):
104
+ raise FileNotFoundError(f"Profile JSON file does not exist: {profile_json}")
105
+ full_profile_filename = os.path.abspath(profile_json)
106
+
107
+ with open(full_profile_filename, "r") as f:
108
+ profile_data = json.load(f)
109
+
110
+ # Check version
111
+ if not bypass_version_check:
112
+ if (
113
+ "profile_cfg" not in profile_data
114
+ or "profile_version" not in profile_data["profile_cfg"]
115
+ ):
116
+ raise KeyError(
117
+ f"ERROR {profile_json} because 'profile_cfg' "
118
+ + f"key is missing in {profile_json}"
119
+ )
120
+
121
+ profile_version = profile_data["profile_cfg"]["profile_version"]
122
+ if (
123
+ profile_version["major"] != 9
124
+ or profile_version["minor"] != 1
125
+ or profile_version["patch"] != 0
126
+ ):
127
+ raise KeyError(
128
+ f"ERROR {profile_json} because 'profile_version' is not "
129
+ + f"supported: {profile_version}"
130
+ )
131
+
132
+ if use_summary:
133
+ with open(summary_json, "r") as f:
134
+ summary_data = json.load(f)
135
+
136
+ summary_file = summary_json
137
+
138
+ iduc = os.path.basename(profile_json)
139
+ iduc = iduc.replace(".json", "")
140
+
141
+ df_row = {
142
+ "id": iduc,
143
+ "profile_name": None,
144
+ "device_clock_Hz": None,
145
+ "core_clock_Hz": None,
146
+ "common_lane_rate_Hz": None,
147
+ "rx_jesd_mode": None,
148
+ "tx_jesd_mode": None,
149
+ # "failed_reason": None,
150
+ "is_8t8r": None,
151
+ "jesd_settings": None,
152
+ "datapath": None,
153
+ # "jif_model": None,
154
+ # "dts_file": None,
155
+ # "bin_filename": None,
156
+ # "hdl_build_id": None,
157
+ }
158
+
159
+ if use_summary:
160
+ if "is_8t8r" not in summary_data:
161
+ raise Exception("AD9088 is not supported")
162
+
163
+ if "is_8t8r" in summary_data and summary_data["is_8t8r"]:
164
+ raise Exception("AD9088 is not supported")
165
+
166
+ else:
167
+ df_row["is_8t8r"] = profile_data["profile_cfg"]["is_8t8r"]
168
+ if df_row["is_8t8r"]:
169
+ raise Exception("AD9088 is not supported")
170
+
171
+ if use_summary:
172
+ device_clock_Hz = summary_data["general_info"]["device_clock_Hz"]
173
+ else:
174
+ device_clock_Hz = profile_data["clk_cfg"]["dev_clk_freq_kHz"] * 1000
175
+ if device_clock_Hz is None:
176
+ raise KeyError(
177
+ f"Skipping {profile_data} because 'device_clock_Hz' key is missing"
178
+ )
179
+ df_row["device_clock_Hz"] = device_clock_Hz
180
+
181
+ if use_summary:
182
+ core_clock_Hz = summary_data["general_info"]["fpga_clock_Hz"]
183
+ if core_clock_Hz is None:
184
+ raise KeyError(
185
+ f"Skipping {profile_data} because 'core_clock_Hz' key is missing"
186
+ )
187
+ df_row["core_clock_Hz"] = core_clock_Hz
188
+
189
+ if use_summary:
190
+ common_lane_rate_Hz = summary_data["general_info"]["common_lane_rate_Hz"]
191
+ if common_lane_rate_Hz is None:
192
+ raise KeyError(
193
+ f"Skipping {profile_data} because 'common_lane_rate_Hz' key is missing"
194
+ )
195
+ else:
196
+ common_lane_rate_Hz = (
197
+ profile_data["jtx"][0]["common_link_cfg"]["lane_rate_kHz"] * 1000
198
+ )
199
+
200
+ df_row["common_lane_rate_Hz"] = common_lane_rate_Hz
201
+
202
+ # Parse datapath config
203
+ path = 0
204
+ if use_summary:
205
+ cddc_decimation = summary_data["rx_routes"][path]["cdrc"]
206
+ fddc_decimation = summary_data["rx_routes"][path]["fdrc"]
207
+ cduc_interpolation = summary_data["tx_routes"][path]["cdrc"]
208
+ fduc_interpolation = summary_data["tx_routes"][path]["fdrc"]
209
+ else:
210
+ cddc_decimation = profile_data["rx_path"][0]["rx_cddc"][0]["drc_ratio"]
211
+ fddc_decimation = profile_data["rx_path"][0]["rx_fddc"][0]["drc_ratio"]
212
+ cduc_interpolation = profile_data["tx_path"][0]["tx_cduc"][0]["drc_ratio"]
213
+ fduc_interpolation = profile_data["tx_path"][0]["tx_fduc"][0]["drc_ratio"]
214
+
215
+ if cddc_decimation == 0:
216
+ cddc_decimation = 1
217
+ elif cddc_decimation == 1:
218
+ cddc_decimation = 2
219
+ elif cddc_decimation == 2:
220
+ cddc_decimation = 3
221
+ elif cddc_decimation == 3:
222
+ cddc_decimation = 4
223
+ elif cddc_decimation == 4:
224
+ cddc_decimation = 6
225
+ elif cddc_decimation == 5:
226
+ cddc_decimation = 12
227
+
228
+ fddc_decimation = int(fddc_decimation)
229
+ fddc_decimation = 2**fddc_decimation
230
+
231
+ cduc_interpolation = int(cduc_interpolation)
232
+
233
+ # THIS IS REALLY BIZARRE but how the profile gen works
234
+ fduc_interpolation = int(fduc_interpolation)
235
+
236
+ df_row["datapath"] = {
237
+ "cddc_decimation": cddc_decimation,
238
+ "fddc_decimation": fddc_decimation,
239
+ "cduc_interpolation": cduc_interpolation,
240
+ "fduc_interpolation": fduc_interpolation,
241
+ }
242
+
243
+ # Parse JESD204 config
244
+ link_index = 0
245
+ lane_index = 0
246
+ rx_jesd_mode = profile_data["jtx"][link_index]["tx_link_cfg"][lane_index][
247
+ "quick_mode_id"
248
+ ]
249
+ tx_jesd_mode = profile_data["jrx"][link_index]["rx_link_cfg"][lane_index][
250
+ "quick_mode_id"
251
+ ]
252
+
253
+ if profile_data["jtx"][link_index]["common_link_cfg"]["ver"] == 0:
254
+ # JESD204B
255
+ raise Exception(f"Skipping {summary_file} because it is a JESD204B profile")
256
+ if profile_data["jrx"][link_index]["common_link_cfg"]["ver"] == 0:
257
+ # JESD204B
258
+ raise Exception(f"Skipping {summary_file} because it is a JESD204B profile")
259
+
260
+ df_row["jesd_settings"] = {}
261
+ for rtx, cfg in zip(["jtx", "jrx"], ["tx_link_cfg", "rx_link_cfg"]):
262
+ df_row["jesd_settings"][rtx] = {}
263
+ for setting, jesd_setting_key in zip(
264
+ ["L", "F", "M", "S", "HD", "K", "N", "Np"],
265
+ [
266
+ "l_minus1",
267
+ "f_minus1",
268
+ "m_minus1",
269
+ "s_minus1",
270
+ "high_dens",
271
+ "k_minus1",
272
+ "n_minus1",
273
+ "np_minus1",
274
+ ],
275
+ ):
276
+ if setting in ["N", "K"]:
277
+ continue
278
+ if jesd_setting_key not in profile_data[rtx][link_index][cfg][lane_index]:
279
+ raise KeyError(
280
+ f"Skipping {summary_file} because {jesd_setting_key} "
281
+ + f"key is missing in {rtx} {cfg}"
282
+ )
283
+ if setting == "HD":
284
+ df_row["jesd_settings"][rtx][setting] = profile_data[rtx][link_index][
285
+ cfg
286
+ ][lane_index][jesd_setting_key]
287
+ else:
288
+ df_row["jesd_settings"][rtx][setting] = (
289
+ int(
290
+ profile_data[rtx][link_index][cfg][lane_index][jesd_setting_key]
291
+ )
292
+ + 1
293
+ )
294
+
295
+ if rx_jesd_mode is None:
296
+ raise KeyError(f"Skipping {summary_file} because 'rx_jesd_mode' key is missing")
297
+ df_row["rx_jesd_mode"] = rx_jesd_mode
298
+ if tx_jesd_mode is None:
299
+ raise KeyError(f"Skipping {summary_file} because 'tx_jesd_mode' key is missing")
300
+ df_row["tx_jesd_mode"] = tx_jesd_mode
301
+ profile_name = os.path.basename(full_profile_filename)
302
+ df_row["profile_name"] = profile_name.replace(".json", "")
303
+
304
+ return df_row
305
+
306
+
307
+ def apply_settings(conv: converter, profile_settings: Dict) -> None:
308
+ """Apply settings to the AD9084 converter.
309
+
310
+ Args:
311
+ conv (converter): The AD9084 converter object.
312
+ profile_settings (Dict): The profile settings dictionary
313
+ containing configuration data.
314
+
315
+ Raises:
316
+ ValueError: If the TX and RX JESD204C modes do not match.
317
+ """
318
+ jesd_class = "jesd204c" # only JESD204C is supported for AD9084 right now
319
+
320
+ cddc_dec = profile_settings["datapath"]["cddc_decimation"]
321
+ cddc_dec = int(cddc_dec)
322
+ fddc_dec = profile_settings["datapath"]["fddc_decimation"]
323
+ fddc_dec = int(fddc_dec)
324
+ converter_rate = int(profile_settings["device_clock_Hz"])
325
+
326
+ conv.sample_clock = converter_rate / (cddc_dec * fddc_dec)
327
+ conv.datapath.cddc_decimations = [cddc_dec] * 4
328
+ conv.datapath.fddc_decimations = [fddc_dec] * 8
329
+ conv.datapath.fddc_enabled = [True] * 8
330
+
331
+ M = profile_settings["jesd_settings"]["jtx"]["M"]
332
+ L = profile_settings["jesd_settings"]["jtx"]["L"]
333
+ S = profile_settings["jesd_settings"]["jtx"]["S"]
334
+ Np = profile_settings["jesd_settings"]["jtx"]["Np"]
335
+
336
+ # Verify TX and RX JESD204C modes are the same
337
+ M_tx = profile_settings["jesd_settings"]["jrx"]["M"]
338
+ L_tx = profile_settings["jesd_settings"]["jrx"]["L"]
339
+ S_tx = profile_settings["jesd_settings"]["jrx"]["S"]
340
+ Np_tx = profile_settings["jesd_settings"]["jrx"]["Np"]
341
+ if M != M_tx or L != L_tx or S != S_tx or Np != Np_tx:
342
+ raise ValueError(
343
+ f"TX and RX JESD204C modes do not match: "
344
+ f"TX (M={M_tx}, L={L_tx}, S={S_tx}, Np={Np_tx}), "
345
+ f"RX (M={M}, L={L}, S={S}, Np={Np})"
346
+ )
347
+
348
+ mode_rx = get_jesd_mode_from_params(
349
+ conv, M=M, L=L, S=S, Np=Np, jesd_class=jesd_class
350
+ )
351
+ assert mode_rx, f"Could not find JESD204C mode for M={M}, L={L}, S={S}, Np={Np}"
352
+ if isinstance(mode_rx, list) and len(mode_rx) > 1:
353
+ print(
354
+ "WARNING: Multiple JESD204C modes found for "
355
+ + f"M={M}, L={L}, S={S}, Np={Np}\n"
356
+ + "Picking the first one."
357
+ )
358
+ mode_rx = mode_rx[0]["mode"]
359
+
360
+ conv.set_quick_configuration_mode(mode_rx, jesd_class)
361
+
362
+
363
+ if __name__ == "__main__":
364
+ data = _load_rx_config_modes()
365
+ # _load_tx_config_modes()