pyadi-jif 0.1.0__py2.py3-none-any.whl → 0.1.1__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 +2 -2
- adijif/clocks/ad9523_1_bf.py +3 -3
- adijif/clocks/ad9528_bf.py +1 -1
- adijif/clocks/hmc7044_bf.py +1 -1
- adijif/clocks/ltc6952.py +2 -2
- adijif/clocks/ltc6952_bf.py +1 -1
- adijif/clocks/ltc6953.py +2 -2
- adijif/converters/__init__.py +12 -1
- adijif/converters/ad9081.py +1 -1
- adijif/converters/ad9081_dp.py +2 -0
- adijif/converters/ad9084.py +29 -7
- adijif/converters/ad9084_draw.py +20 -14
- adijif/converters/ad9084_util.py +24 -5
- adijif/converters/ad9088_dp.py +111 -0
- adijif/converters/converter.py +153 -2
- adijif/draw.py +11 -2
- adijif/fpgas/xilinx/__init__.py +4 -2
- adijif/jesd.py +3 -1
- adijif/plls/adf4030.py +6 -1
- adijif/plls/adf4382.py +0 -1
- adijif/solvers.py +5 -4
- adijif/system.py +16 -1
- adijif/tools/explorer/cli.py +36 -0
- adijif/tools/explorer/main.py +41 -0
- adijif/tools/explorer/src/pages/__init__.py +16 -0
- adijif/tools/explorer/src/pages/clockconfigurator.py +193 -0
- adijif/tools/explorer/src/pages/helpers/datapath.py +85 -0
- adijif/tools/explorer/src/pages/helpers/drawers.py +87 -0
- adijif/tools/explorer/src/pages/helpers/jesd.py +140 -0
- adijif/tools/explorer/src/pages/jesdmodeselector.py +209 -0
- adijif/tools/explorer/src/pages/systemconfigurator.py +252 -0
- adijif/tools/explorer/src/state.py +141 -0
- adijif/tools/explorer/src/utils.py +37 -0
- adijif/types.py +23 -4
- {pyadi_jif-0.1.0.dist-info → pyadi_jif-0.1.1.dist-info}/METADATA +36 -12
- {pyadi_jif-0.1.0.dist-info → pyadi_jif-0.1.1.dist-info}/RECORD +41 -30
- pyadi_jif-0.1.1.dist-info/entry_points.txt +2 -0
- adijif/d2/__init__.py +0 -26
- adijif/d2/d2lib.h +0 -81
- {pyadi_jif-0.1.0.dist-info → pyadi_jif-0.1.1.dist-info}/WHEEL +0 -0
- {pyadi_jif-0.1.0.dist-info → pyadi_jif-0.1.1.dist-info}/licenses/AUTHORS.rst +0 -0
- {pyadi_jif-0.1.0.dist-info → pyadi_jif-0.1.1.dist-info}/licenses/LICENSE +0 -0
- {pyadi_jif-0.1.0.dist-info → pyadi_jif-0.1.1.dist-info}/top_level.txt +0 -0
adijif/converters/converter.py
CHANGED
|
@@ -24,6 +24,9 @@ class converter(core, jesd, gekko_translation, metaclass=ABCMeta):
|
|
|
24
24
|
"""DSP data path of device"""
|
|
25
25
|
datapath = None
|
|
26
26
|
|
|
27
|
+
"""Show rates on drawing diagrams."""
|
|
28
|
+
show_rates = True
|
|
29
|
+
|
|
27
30
|
config: Dict = {}
|
|
28
31
|
_jesd_params_to_skip = [
|
|
29
32
|
"E",
|
|
@@ -36,10 +39,10 @@ class converter(core, jesd, gekko_translation, metaclass=ABCMeta):
|
|
|
36
39
|
"global_index",
|
|
37
40
|
]
|
|
38
41
|
|
|
39
|
-
def
|
|
42
|
+
def draw_dac(
|
|
40
43
|
self, clocks: Dict, lo: Layout = None, clock_chip_node: Node = None
|
|
41
44
|
) -> str:
|
|
42
|
-
"""Generic Draw converter model.
|
|
45
|
+
"""Generic Draw converter DAC model.
|
|
43
46
|
|
|
44
47
|
Args:
|
|
45
48
|
clocks (Dict): Clocking configuration
|
|
@@ -57,6 +60,7 @@ class converter(core, jesd, gekko_translation, metaclass=ABCMeta):
|
|
|
57
60
|
|
|
58
61
|
if not system_draw:
|
|
59
62
|
lo = Layout(f"{name} Example")
|
|
63
|
+
lo.show_rates = self.show_rates
|
|
60
64
|
else:
|
|
61
65
|
assert isinstance(lo, Layout), "lo must be a Layout object"
|
|
62
66
|
|
|
@@ -102,9 +106,133 @@ class converter(core, jesd, gekko_translation, metaclass=ABCMeta):
|
|
|
102
106
|
|
|
103
107
|
lo.add_connection({"from": ref_in, "to": ic_node, "rate": rate})
|
|
104
108
|
|
|
109
|
+
# Add deframer
|
|
110
|
+
jesd204_deframer = Node("JESD204 Deframer", ntype="jesd204deframer")
|
|
111
|
+
ic_node.add_child(jesd204_deframer)
|
|
112
|
+
|
|
113
|
+
# Add DAC
|
|
114
|
+
dac = Node("DAC", ntype="dac")
|
|
115
|
+
ic_node.add_child(dac)
|
|
116
|
+
ic_node.add_connection({"from": jesd204_deframer, "to": dac})
|
|
117
|
+
|
|
118
|
+
# SYSREF
|
|
119
|
+
if not system_draw:
|
|
120
|
+
sysref_in = Node("SYSREF_IN", ntype="input")
|
|
121
|
+
lo.add_connection(
|
|
122
|
+
{
|
|
123
|
+
"from": sysref_in,
|
|
124
|
+
"to": jesd204_deframer,
|
|
125
|
+
# "rate": clocks[f"{name}_sysref"],
|
|
126
|
+
"rate": clocks[sysref_clk_name],
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
else:
|
|
130
|
+
# to_node = lo.get_node(f"{name}_sysref")
|
|
131
|
+
to_node = lo.get_node(sysref_clk_name)
|
|
132
|
+
from_node = lo.get_connection(to=to_node.name)
|
|
133
|
+
assert from_node, "No connection found"
|
|
134
|
+
assert isinstance(from_node, list), "Connection must be a list"
|
|
135
|
+
assert len(from_node) == 1, "Only one connection allowed"
|
|
136
|
+
sysref_in = from_node[0]["from"]
|
|
137
|
+
lo.remove_node(to_node.name)
|
|
138
|
+
|
|
139
|
+
lo.add_connection(
|
|
140
|
+
{
|
|
141
|
+
"from": sysref_in,
|
|
142
|
+
"to": jesd204_deframer,
|
|
143
|
+
# "rate": clocks[f"{name}_sysref"],
|
|
144
|
+
"rate": clocks[sysref_clk_name],
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# WIP Add remote framer
|
|
149
|
+
jesd204_framer = Node("JESD204 Framer", ntype="framer")
|
|
150
|
+
|
|
151
|
+
# Add connect for each lane
|
|
152
|
+
for _ in range(self.L):
|
|
153
|
+
lane_rate = self.bit_clock
|
|
154
|
+
lo.add_connection(
|
|
155
|
+
{"from": jesd204_framer, "to": jesd204_deframer, "rate": lane_rate}
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
if not system_draw:
|
|
159
|
+
return lo.draw()
|
|
160
|
+
|
|
161
|
+
def draw_adc(
|
|
162
|
+
self, clocks: Dict, lo: Layout = None, clock_chip_node: Node = None
|
|
163
|
+
) -> str:
|
|
164
|
+
"""Generic Draw converter ADC model.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
clocks (Dict): Clocking configuration
|
|
168
|
+
lo (Layout): Layout object to draw on
|
|
169
|
+
clock_chip_node (Node): Clock chip node to add to. Defaults to None.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
str: Path to image file
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
Exception: If no solution is saved
|
|
176
|
+
"""
|
|
177
|
+
system_draw = lo is not None
|
|
178
|
+
name = self.name.lower()
|
|
179
|
+
|
|
180
|
+
if not system_draw:
|
|
181
|
+
lo = Layout(f"{name} Example")
|
|
182
|
+
lo.show_rates = self.show_rates
|
|
183
|
+
else:
|
|
184
|
+
assert isinstance(lo, Layout), "lo must be a Layout object"
|
|
185
|
+
|
|
186
|
+
ic_node = Node(self.name)
|
|
187
|
+
lo.add_node(ic_node)
|
|
188
|
+
|
|
189
|
+
# rate = clocks[f"{name}_ref_clk"]
|
|
190
|
+
# Find key with ending
|
|
191
|
+
ref_clk_name = None
|
|
192
|
+
for key in clocks.keys():
|
|
193
|
+
if key.lower().endswith(f"{name.lower()}_ref_clk"):
|
|
194
|
+
ref_clk_name = key
|
|
195
|
+
break
|
|
196
|
+
if ref_clk_name is None:
|
|
197
|
+
raise Exception(
|
|
198
|
+
f"No clock found for {name}_ref_clk\n.Options: {clocks.keys()}"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
sysref_clk_name = None
|
|
202
|
+
for key in clocks.keys():
|
|
203
|
+
if key.lower().endswith(f"{name.lower()}_sysref"):
|
|
204
|
+
sysref_clk_name = key
|
|
205
|
+
break
|
|
206
|
+
if sysref_clk_name is None:
|
|
207
|
+
raise Exception(
|
|
208
|
+
f"No clock found for {name}_sysref\n.Options: {clocks.keys()}"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
if not system_draw:
|
|
212
|
+
ref_in = Node("REF_IN", ntype="input")
|
|
213
|
+
lo.add_node(ref_in)
|
|
214
|
+
else:
|
|
215
|
+
to_node = lo.get_node(ref_clk_name)
|
|
216
|
+
from_node = lo.get_connection(to=to_node.name)
|
|
217
|
+
assert from_node, "No connection found"
|
|
218
|
+
assert isinstance(from_node, list), "Connection must be a list"
|
|
219
|
+
assert len(from_node) == 1, "Only one connection allowed"
|
|
220
|
+
ref_in = from_node[0]["from"]
|
|
221
|
+
# Remove to_node since it is not needed
|
|
222
|
+
lo.remove_node(to_node.name)
|
|
223
|
+
|
|
224
|
+
rate = clocks[ref_clk_name]
|
|
225
|
+
|
|
226
|
+
lo.add_connection({"from": ref_in, "to": ic_node, "rate": rate})
|
|
227
|
+
|
|
228
|
+
# Add ADC
|
|
229
|
+
adc = Node("ADC", ntype="adc")
|
|
230
|
+
ic_node.add_child(adc)
|
|
231
|
+
|
|
105
232
|
# Add framer
|
|
106
233
|
jesd204_framer = Node("JESD204 Framer", ntype="jesd204framer")
|
|
107
234
|
ic_node.add_child(jesd204_framer)
|
|
235
|
+
ic_node.add_connection({"from": adc, "to": jesd204_framer})
|
|
108
236
|
|
|
109
237
|
# SYSREF
|
|
110
238
|
if not system_draw:
|
|
@@ -149,6 +277,29 @@ class converter(core, jesd, gekko_translation, metaclass=ABCMeta):
|
|
|
149
277
|
if not system_draw:
|
|
150
278
|
return lo.draw()
|
|
151
279
|
|
|
280
|
+
def draw(
|
|
281
|
+
self, clocks: Dict, lo: Layout = None, clock_chip_node: Node = None
|
|
282
|
+
) -> str:
|
|
283
|
+
"""Generic Draw converter model.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
clocks (Dict): Clocking configuration
|
|
287
|
+
lo (Layout): Layout object to draw on
|
|
288
|
+
clock_chip_node (Node): Clock chip node to add to. Defaults to None.
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
str: Path to image file
|
|
292
|
+
|
|
293
|
+
Raises:
|
|
294
|
+
Exception: If no solution is saved
|
|
295
|
+
"""
|
|
296
|
+
if self.converter_type.lower() == "adc":
|
|
297
|
+
return self.draw_adc(clocks, lo, clock_chip_node)
|
|
298
|
+
elif self.converter_type.lower() == "dac":
|
|
299
|
+
return self.draw_dac(clocks, lo, clock_chip_node)
|
|
300
|
+
else:
|
|
301
|
+
raise Exception("Unknown converter type")
|
|
302
|
+
|
|
152
303
|
def validate_config(self) -> None:
|
|
153
304
|
"""Validate device configuration including JESD and clocks.
|
|
154
305
|
|
adijif/draw.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
+
from importlib.util import find_spec
|
|
6
7
|
from typing import Union
|
|
7
8
|
|
|
8
9
|
connection_style_types = {
|
|
@@ -191,7 +192,7 @@ class Layout:
|
|
|
191
192
|
|
|
192
193
|
si = " "
|
|
193
194
|
use_d2_cli = False
|
|
194
|
-
_write_out_d2_file =
|
|
195
|
+
_write_out_d2_file = False
|
|
195
196
|
|
|
196
197
|
def __init__(self, name: str) -> None:
|
|
197
198
|
"""Initialize layout with name.
|
|
@@ -313,6 +314,9 @@ class Layout:
|
|
|
313
314
|
|
|
314
315
|
Returns:
|
|
315
316
|
str: Path to the output image file.
|
|
317
|
+
|
|
318
|
+
Raises:
|
|
319
|
+
Exception: d2 support not installed.
|
|
316
320
|
"""
|
|
317
321
|
diag = "direction: right\n\n"
|
|
318
322
|
|
|
@@ -488,7 +492,12 @@ class Layout:
|
|
|
488
492
|
print(f"Saved to {self.output_filename}")
|
|
489
493
|
|
|
490
494
|
# Use bindings
|
|
491
|
-
|
|
495
|
+
if find_spec("d2"):
|
|
496
|
+
from d2 import compile
|
|
497
|
+
else:
|
|
498
|
+
raise Exception(
|
|
499
|
+
"d2 support not installed. Please install package pyd2lang-native"
|
|
500
|
+
)
|
|
492
501
|
|
|
493
502
|
out = compile(diag)
|
|
494
503
|
# with open(self.output_image_filename, "w") as f:
|
adijif/fpgas/xilinx/__init__.py
CHANGED
|
@@ -280,6 +280,8 @@ class xilinx(xilinx_bf, xilinx_draw):
|
|
|
280
280
|
"""
|
|
281
281
|
return self._ref_clock_constraint
|
|
282
282
|
|
|
283
|
+
_ref_clock_constraint_options = ["CORE_CLOCK", "CORE_CLOCK_DIV2", "Unconstrained"]
|
|
284
|
+
|
|
283
285
|
@ref_clock_constraint.setter
|
|
284
286
|
def ref_clock_constraint(self, value: str) -> None:
|
|
285
287
|
"""Set reference clock constraint.
|
|
@@ -295,10 +297,10 @@ class xilinx(xilinx_bf, xilinx_draw):
|
|
|
295
297
|
Raises:
|
|
296
298
|
Exception: Invalid ref_clock_constraint selection.
|
|
297
299
|
"""
|
|
298
|
-
if value not in
|
|
300
|
+
if value not in self._ref_clock_constraint_options:
|
|
299
301
|
raise Exception(
|
|
300
302
|
f"Invalid ref_clock_constraint {value}, "
|
|
301
|
-
+ "options are
|
|
303
|
+
+ f"options are {self._ref_clock_constraint_options}"
|
|
302
304
|
)
|
|
303
305
|
self._ref_clock_constraint = value
|
|
304
306
|
|
adijif/jesd.py
CHANGED
|
@@ -631,7 +631,9 @@ class jesd(metaclass=ABCMeta):
|
|
|
631
631
|
if int(value) != value:
|
|
632
632
|
raise Exception("Np must be an integer")
|
|
633
633
|
if value not in self.Np_available:
|
|
634
|
-
|
|
634
|
+
msg = "Np not in range for device. "
|
|
635
|
+
msg += f"Got {value}, valid are {self.Np_available}"
|
|
636
|
+
raise Exception(msg)
|
|
635
637
|
self._Np = value
|
|
636
638
|
|
|
637
639
|
# DERIVED SCALERS
|
adijif/plls/adf4030.py
CHANGED
|
@@ -4,6 +4,7 @@ from typing import Dict, List, Union
|
|
|
4
4
|
|
|
5
5
|
from docplex.cp.solution import CpoSolveResult # type: ignore
|
|
6
6
|
|
|
7
|
+
from adijif.clocks.clock import clock as clockc
|
|
7
8
|
from adijif.plls.pll import pll
|
|
8
9
|
from adijif.solvers import CpoExpr, GK_Intermediate
|
|
9
10
|
|
|
@@ -193,11 +194,15 @@ class adf4030(pll):
|
|
|
193
194
|
]
|
|
194
195
|
)
|
|
195
196
|
|
|
196
|
-
def _setup(self, input_ref: int) -> None:
|
|
197
|
+
def _setup(self, input_ref: Union[int, clockc]) -> None:
|
|
197
198
|
if isinstance(input_ref, (float, int)):
|
|
198
199
|
assert (
|
|
199
200
|
self.input_freq_max >= input_ref >= self.input_freq_min
|
|
200
201
|
), "Input frequency out of range"
|
|
202
|
+
else:
|
|
203
|
+
self._add_equation(
|
|
204
|
+
[input_ref <= self.input_freq_max, input_ref >= self.input_freq_min]
|
|
205
|
+
)
|
|
201
206
|
|
|
202
207
|
# Setup clock chip internal constraints
|
|
203
208
|
self._setup_solver_constraints(input_ref)
|
adijif/plls/adf4382.py
CHANGED
adijif/solvers.py
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
# pytype: skip-file
|
|
3
3
|
"""Common solver API management layer."""
|
|
4
4
|
|
|
5
|
+
from importlib.util import find_spec
|
|
5
6
|
from typing import Union
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
if find_spec("docplex"):
|
|
8
9
|
from docplex.cp.expression import CpoExpr # type: ignore
|
|
9
10
|
from docplex.cp.expression import CpoFunctionCall # type: ignore
|
|
10
11
|
from docplex.cp.expression import CpoIntVar # type: ignore
|
|
@@ -13,7 +14,7 @@ try:
|
|
|
13
14
|
from docplex.cp.solution import CpoSolveResult # type: ignore
|
|
14
15
|
|
|
15
16
|
cplex_solver = True
|
|
16
|
-
|
|
17
|
+
else:
|
|
17
18
|
cplex_solver = False
|
|
18
19
|
CpoExpr = None
|
|
19
20
|
CpoFunctionCall = None
|
|
@@ -22,7 +23,7 @@ except ImportError:
|
|
|
22
23
|
continuous_var = None
|
|
23
24
|
interval_var = None
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
if find_spec("gekko"):
|
|
26
27
|
import gekko # type: ignore
|
|
27
28
|
from gekko import GEKKO # type: ignore
|
|
28
29
|
from gekko.gk_operators import GK_Intermediate # type: ignore
|
|
@@ -30,7 +31,7 @@ try:
|
|
|
30
31
|
from gekko.gk_variable import GKVariable # type: ignore
|
|
31
32
|
|
|
32
33
|
gekko_solver = True
|
|
33
|
-
|
|
34
|
+
else:
|
|
34
35
|
gekko_solver = False
|
|
35
36
|
gekko = None
|
|
36
37
|
GEKKO = None
|
adijif/system.py
CHANGED
|
@@ -330,7 +330,8 @@ class system(SystemPLL, system_draw):
|
|
|
330
330
|
|
|
331
331
|
# Setup external sysref PLLs
|
|
332
332
|
for pll in self._plls_sysref:
|
|
333
|
-
|
|
333
|
+
if not isinstance(pll._ref, clockc):
|
|
334
|
+
pll._setup(pll._ref)
|
|
334
335
|
|
|
335
336
|
for conv in convs:
|
|
336
337
|
if conv._nested: # MxFE, Transceivers
|
|
@@ -368,6 +369,20 @@ class system(SystemPLL, system_draw):
|
|
|
368
369
|
if conv.name + "_ref_clk" in config:
|
|
369
370
|
raise Exception("Duplicate converter names found")
|
|
370
371
|
|
|
372
|
+
# Setup sysref generator is separate (ADF4030) and driven by clock-chip
|
|
373
|
+
for pll_sr in self._plls_sysref:
|
|
374
|
+
if isinstance(pll._ref, clockc):
|
|
375
|
+
for name in pll_sr._connected_to_output:
|
|
376
|
+
if name == conv.name:
|
|
377
|
+
# Get clock from clockchip
|
|
378
|
+
if conv._nested:
|
|
379
|
+
raise Exception("Nested converters not supported")
|
|
380
|
+
else:
|
|
381
|
+
config, clock_names = self._get_ref_clock(
|
|
382
|
+
pll_sr, config, clock_names
|
|
383
|
+
)
|
|
384
|
+
pll_sr._setup(config[pll_sr.name + "_ref_clk"])
|
|
385
|
+
|
|
371
386
|
# Ask clock chip for converter ref
|
|
372
387
|
config, clock_names = self._get_ref_clock(conv, config, clock_names)
|
|
373
388
|
config, sys_ref_names = self._get_sysref_clock(
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""CLI entry point for PyADI-JIF Tools Explorer."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from streamlit.web import cli as stcli
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def run_streamlit(args: Optional[list[str]] = None) -> None:
|
|
11
|
+
"""Run the Streamlit tools explorer app."""
|
|
12
|
+
# Get the directory where this file is located
|
|
13
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
14
|
+
main_file = os.path.join(current_dir, "main.py")
|
|
15
|
+
|
|
16
|
+
# Get arguments from sys.argv if not provided
|
|
17
|
+
if args is None:
|
|
18
|
+
args = sys.argv[1:]
|
|
19
|
+
|
|
20
|
+
# Set up sys.argv for streamlit
|
|
21
|
+
sys.argv = [
|
|
22
|
+
"streamlit",
|
|
23
|
+
"run",
|
|
24
|
+
main_file,
|
|
25
|
+
"--logger.level=error",
|
|
26
|
+
"--browser.gatherUsageStats=false",
|
|
27
|
+
"--server.showEmailPrompt=false",
|
|
28
|
+
] + args
|
|
29
|
+
|
|
30
|
+
# Run streamlit
|
|
31
|
+
sys.exit(stcli.main())
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
args = sys.argv[1:]
|
|
36
|
+
run_streamlit(args)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Main entry point for PyADI-JIF Tools Explorer Streamlit application."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import streamlit as st
|
|
8
|
+
from src.pages import PAGE_MAP
|
|
9
|
+
from src.utils import add_custom_css
|
|
10
|
+
|
|
11
|
+
# from src.state import provide_state
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Global logging configuration
|
|
15
|
+
logging.basicConfig(level=logging.ERROR)
|
|
16
|
+
|
|
17
|
+
# Configure page settings - must be first Streamlit command
|
|
18
|
+
st.set_page_config(
|
|
19
|
+
page_title="PyADI-JIF Tools Explorer",
|
|
20
|
+
page_icon=os.path.join(os.path.dirname(__file__), "PyADI-JIF_logo.png"),
|
|
21
|
+
initial_sidebar_state="expanded",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Apply custom CSS to main page and sidebar
|
|
25
|
+
add_custom_css()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# @provide_state()
|
|
29
|
+
def main(state: Optional[object] = None) -> None:
|
|
30
|
+
"""Run the main Streamlit application."""
|
|
31
|
+
logo_image = os.path.join(os.path.dirname(__file__), "PyADI-JIF_logo.png")
|
|
32
|
+
st.sidebar.image(logo_image)
|
|
33
|
+
st.sidebar.title("Tools Explorer")
|
|
34
|
+
current_page = st.sidebar.radio(
|
|
35
|
+
"Select a Tool", list(PAGE_MAP), label_visibility="hidden"
|
|
36
|
+
)
|
|
37
|
+
PAGE_MAP[current_page](state=state).write()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
main()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Page definitions for PyADI-JIF Tools Explorer."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, Type
|
|
4
|
+
|
|
5
|
+
from ..utils import Page
|
|
6
|
+
from .clockconfigurator import ClockConfigurator
|
|
7
|
+
from .jesdmodeselector import JESDModeSelector
|
|
8
|
+
from .systemconfigurator import SystemConfigurator
|
|
9
|
+
|
|
10
|
+
PAGE_MAP: Dict[str, Type[Page]] = {
|
|
11
|
+
"JESD204 Mode Selector": JESDModeSelector,
|
|
12
|
+
"Clock Configurator": ClockConfigurator,
|
|
13
|
+
"System Configurator": SystemConfigurator,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
__all__ = ["PAGE_MAP"]
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Clock configurator page for PyADI-JIF Tools Explorer."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import streamlit as st
|
|
7
|
+
|
|
8
|
+
from adijif.clocks import supported_parts as sp
|
|
9
|
+
|
|
10
|
+
from ..utils import Page
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
import adidt as dt
|
|
14
|
+
except ImportError:
|
|
15
|
+
dt = None
|
|
16
|
+
|
|
17
|
+
log = logging.getLogger(__name__)
|
|
18
|
+
log.setLevel(logging.ERROR)
|
|
19
|
+
|
|
20
|
+
# Parse parts and options to skip
|
|
21
|
+
options_to_skip = ["list_references_available", "d_syspulse"]
|
|
22
|
+
parts_to_ignore = ["ad9545", "ad9523_1"]
|
|
23
|
+
sp = [p for p in sp if p not in parts_to_ignore]
|
|
24
|
+
sp = [p for p in sp if p != "hmc7044"]
|
|
25
|
+
sp.insert(0, "hmc7044")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ClockConfigurator(Page):
|
|
29
|
+
"""Clock configurator tool page."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, state: Optional[object]) -> None:
|
|
32
|
+
"""Initialize clock configurator page.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
state: Application state object
|
|
36
|
+
"""
|
|
37
|
+
self.state = state
|
|
38
|
+
|
|
39
|
+
def write(self) -> None:
|
|
40
|
+
"""Render the clock configurator page."""
|
|
41
|
+
st.title("Clock Configurator")
|
|
42
|
+
# sp = ["hmc7044"]
|
|
43
|
+
|
|
44
|
+
sb = st.selectbox(
|
|
45
|
+
label="Select a part",
|
|
46
|
+
options=sp,
|
|
47
|
+
format_func=lambda x: x.upper().replace("_", "-"),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
with st.expander("Clock Inputs and Outputs", expanded=True):
|
|
51
|
+
reference = st.number_input(
|
|
52
|
+
"Reference Clock",
|
|
53
|
+
value=125000000,
|
|
54
|
+
min_value=1,
|
|
55
|
+
max_value=int(1e9),
|
|
56
|
+
step=1,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
with st.container(border=True):
|
|
60
|
+
num_outputs = st.number_input(
|
|
61
|
+
"Number of Clock Outputs",
|
|
62
|
+
value=2,
|
|
63
|
+
min_value=1,
|
|
64
|
+
max_value=10,
|
|
65
|
+
step=1,
|
|
66
|
+
)
|
|
67
|
+
outputs = []
|
|
68
|
+
output_names = []
|
|
69
|
+
for i in range(num_outputs):
|
|
70
|
+
columns = st.columns(2)
|
|
71
|
+
with columns[0]:
|
|
72
|
+
outputs.append(
|
|
73
|
+
st.number_input(
|
|
74
|
+
f"Output Clock {i+1}",
|
|
75
|
+
value=125000000,
|
|
76
|
+
min_value=1,
|
|
77
|
+
max_value=int(1e9),
|
|
78
|
+
step=1,
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
with columns[1]:
|
|
82
|
+
output_names.append(
|
|
83
|
+
st.text_input(f"Output Clock Name {i+1}", f"CLK{i+1}")
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
import adijif # noqa: F401
|
|
87
|
+
|
|
88
|
+
class_def = eval(f"adijif.{sb}") # noqa: S307
|
|
89
|
+
props = dir(class_def)
|
|
90
|
+
|
|
91
|
+
props = [p for p in props if not p.startswith("__")]
|
|
92
|
+
props = [p for p in props if not p.startswith("_")]
|
|
93
|
+
configurable_props = [
|
|
94
|
+
p.replace("_available", "") for p in props if "_available" in p
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
prop_and_options = {}
|
|
98
|
+
for prop in configurable_props:
|
|
99
|
+
if prop in options_to_skip or prop + "_available" not in props:
|
|
100
|
+
continue
|
|
101
|
+
prop_and_options[prop] = getattr(class_def, prop + "_available")
|
|
102
|
+
|
|
103
|
+
selections = {}
|
|
104
|
+
with st.expander("Internal Clock Configuration"):
|
|
105
|
+
for prop, options in prop_and_options.items():
|
|
106
|
+
prop_docstring = getattr(class_def, prop).__doc__
|
|
107
|
+
with st.container(border=True):
|
|
108
|
+
label = f"""
|
|
109
|
+
{prop} : {prop_docstring}"""
|
|
110
|
+
if len(options) > 16:
|
|
111
|
+
v_min, v_max = min(options), max(options)
|
|
112
|
+
start, end = st.select_slider(
|
|
113
|
+
label, options, value=(v_min, v_max)
|
|
114
|
+
)
|
|
115
|
+
selections[prop] = {"start": start, "end": end}
|
|
116
|
+
else:
|
|
117
|
+
selections[prop] = st.multiselect(label, options)
|
|
118
|
+
|
|
119
|
+
clk_chip = eval(f"adijif.{sb}()") # noqa: S307
|
|
120
|
+
|
|
121
|
+
for prop, values in selections.items():
|
|
122
|
+
if isinstance(values, dict):
|
|
123
|
+
props_available = getattr(clk_chip, prop + "_available")
|
|
124
|
+
if (
|
|
125
|
+
min(props_available) == values["start"]
|
|
126
|
+
and max(props_available) == values["end"]
|
|
127
|
+
):
|
|
128
|
+
continue
|
|
129
|
+
picked = [
|
|
130
|
+
v for v in props_available if values["start"] <= v <= values["end"]
|
|
131
|
+
]
|
|
132
|
+
setattr(clk_chip, prop, picked)
|
|
133
|
+
else:
|
|
134
|
+
if not values:
|
|
135
|
+
continue
|
|
136
|
+
setattr(clk_chip, prop, values)
|
|
137
|
+
|
|
138
|
+
output_clocks = outputs
|
|
139
|
+
output_clocks = list(map(int, output_clocks)) # force to be ints
|
|
140
|
+
|
|
141
|
+
# Remove duplicates
|
|
142
|
+
output_clocks_filtered = output_clocks
|
|
143
|
+
output_names_filtered = output_names
|
|
144
|
+
|
|
145
|
+
clk_chip.set_requested_clocks(
|
|
146
|
+
reference, output_clocks_filtered, output_names_filtered
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
clk_chip.solve()
|
|
151
|
+
o = clk_chip.get_config()
|
|
152
|
+
log.info("Solution found")
|
|
153
|
+
log.info(o)
|
|
154
|
+
|
|
155
|
+
# if dt:
|
|
156
|
+
# columns_results = st.columns(2)
|
|
157
|
+
|
|
158
|
+
# with columns_results[0]:
|
|
159
|
+
# with st.expander("Found Configuration"):
|
|
160
|
+
# st.write(o)
|
|
161
|
+
|
|
162
|
+
# with columns_results[1]:
|
|
163
|
+
# if sb == "hmc7044":
|
|
164
|
+
|
|
165
|
+
# clk = dt.hmc7044_dt(offline=True)
|
|
166
|
+
# dtsi = clk.map_config_to_fragment(o)
|
|
167
|
+
# with st.expander("Device Tree Fragment"):
|
|
168
|
+
# st.code(dtsi)
|
|
169
|
+
# else:
|
|
170
|
+
# with st.expander("Found Configuration"):
|
|
171
|
+
# st.write(o)
|
|
172
|
+
|
|
173
|
+
config_out = o
|
|
174
|
+
image_data = clk_chip.draw()
|
|
175
|
+
|
|
176
|
+
warning = False
|
|
177
|
+
|
|
178
|
+
except Exception as e:
|
|
179
|
+
log.warning(e)
|
|
180
|
+
|
|
181
|
+
warning = True
|
|
182
|
+
|
|
183
|
+
with st.expander("Found Configuration", expanded=True):
|
|
184
|
+
if warning:
|
|
185
|
+
st.warning("No valid configuration found")
|
|
186
|
+
else:
|
|
187
|
+
st.write(config_out)
|
|
188
|
+
|
|
189
|
+
with st.expander("Diagram", expanded=True):
|
|
190
|
+
if warning:
|
|
191
|
+
st.warning("No diagram to show")
|
|
192
|
+
else:
|
|
193
|
+
st.image(image_data, width="stretch")
|