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.
Files changed (43) hide show
  1. adijif/__init__.py +2 -2
  2. adijif/clocks/ad9523_1_bf.py +3 -3
  3. adijif/clocks/ad9528_bf.py +1 -1
  4. adijif/clocks/hmc7044_bf.py +1 -1
  5. adijif/clocks/ltc6952.py +2 -2
  6. adijif/clocks/ltc6952_bf.py +1 -1
  7. adijif/clocks/ltc6953.py +2 -2
  8. adijif/converters/__init__.py +12 -1
  9. adijif/converters/ad9081.py +1 -1
  10. adijif/converters/ad9081_dp.py +2 -0
  11. adijif/converters/ad9084.py +29 -7
  12. adijif/converters/ad9084_draw.py +20 -14
  13. adijif/converters/ad9084_util.py +24 -5
  14. adijif/converters/ad9088_dp.py +111 -0
  15. adijif/converters/converter.py +153 -2
  16. adijif/draw.py +11 -2
  17. adijif/fpgas/xilinx/__init__.py +4 -2
  18. adijif/jesd.py +3 -1
  19. adijif/plls/adf4030.py +6 -1
  20. adijif/plls/adf4382.py +0 -1
  21. adijif/solvers.py +5 -4
  22. adijif/system.py +16 -1
  23. adijif/tools/explorer/cli.py +36 -0
  24. adijif/tools/explorer/main.py +41 -0
  25. adijif/tools/explorer/src/pages/__init__.py +16 -0
  26. adijif/tools/explorer/src/pages/clockconfigurator.py +193 -0
  27. adijif/tools/explorer/src/pages/helpers/datapath.py +85 -0
  28. adijif/tools/explorer/src/pages/helpers/drawers.py +87 -0
  29. adijif/tools/explorer/src/pages/helpers/jesd.py +140 -0
  30. adijif/tools/explorer/src/pages/jesdmodeselector.py +209 -0
  31. adijif/tools/explorer/src/pages/systemconfigurator.py +252 -0
  32. adijif/tools/explorer/src/state.py +141 -0
  33. adijif/tools/explorer/src/utils.py +37 -0
  34. adijif/types.py +23 -4
  35. {pyadi_jif-0.1.0.dist-info → pyadi_jif-0.1.1.dist-info}/METADATA +36 -12
  36. {pyadi_jif-0.1.0.dist-info → pyadi_jif-0.1.1.dist-info}/RECORD +41 -30
  37. pyadi_jif-0.1.1.dist-info/entry_points.txt +2 -0
  38. adijif/d2/__init__.py +0 -26
  39. adijif/d2/d2lib.h +0 -81
  40. {pyadi_jif-0.1.0.dist-info → pyadi_jif-0.1.1.dist-info}/WHEEL +0 -0
  41. {pyadi_jif-0.1.0.dist-info → pyadi_jif-0.1.1.dist-info}/licenses/AUTHORS.rst +0 -0
  42. {pyadi_jif-0.1.0.dist-info → pyadi_jif-0.1.1.dist-info}/licenses/LICENSE +0 -0
  43. {pyadi_jif-0.1.0.dist-info → pyadi_jif-0.1.1.dist-info}/top_level.txt +0 -0
@@ -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 draw(
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 = True
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
- from .d2 import compile
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:
@@ -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 ["CORE_CLOCK", "CORE_CLOCK_DIV2", "Unconstrained"]:
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 CORE_CLOCK, CORE_CLOCK_DIV2, Unconstrained"
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
- raise Exception("Np not in range for device")
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
@@ -576,6 +576,5 @@ class adf4382(pll):
576
576
  """
577
577
  self._setup(ref_in)
578
578
  self._clk_names = ["rf_out"]
579
- print(f"Output: {out_freq}")
580
579
 
581
580
  self._add_equation([self.config["o"] * out_freq == self.config["vco"]])
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
- try:
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
- except ImportError:
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
- try:
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
- except ImportError:
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
- pll._setup(pll._ref)
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")