siliconcompiler 0.29.0__py3-none-any.whl → 0.29.1__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 (87) hide show
  1. siliconcompiler/_metadata.py +1 -1
  2. siliconcompiler/apps/__init__.py +26 -0
  3. siliconcompiler/apps/utils/replay.py +96 -38
  4. siliconcompiler/checklists/__init__.py +12 -0
  5. siliconcompiler/core.py +75 -10
  6. siliconcompiler/flows/__init__.py +34 -0
  7. siliconcompiler/flows/showflow.py +1 -1
  8. siliconcompiler/libs/__init__.py +5 -0
  9. siliconcompiler/optimizer/__init__.py +199 -0
  10. siliconcompiler/optimizer/vizier.py +259 -0
  11. siliconcompiler/pdks/__init__.py +5 -0
  12. siliconcompiler/scheduler/__init__.py +67 -49
  13. siliconcompiler/scheduler/send_messages.py +1 -1
  14. siliconcompiler/schema/schema_cfg.py +2 -2
  15. siliconcompiler/schema/schema_obj.py +13 -10
  16. siliconcompiler/schema/utils.py +2 -0
  17. siliconcompiler/sphinx_ext/__init__.py +85 -0
  18. siliconcompiler/sphinx_ext/dynamicgen.py +17 -33
  19. siliconcompiler/sphinx_ext/schemagen.py +3 -2
  20. siliconcompiler/targets/__init__.py +26 -0
  21. siliconcompiler/templates/replay/replay.py.j2 +62 -0
  22. siliconcompiler/templates/replay/requirements.txt +2 -1
  23. siliconcompiler/templates/replay/setup.sh +119 -6
  24. siliconcompiler/tools/__init__.py +60 -0
  25. siliconcompiler/tools/_common/asic.py +7 -6
  26. siliconcompiler/tools/ghdl/ghdl.py +1 -2
  27. siliconcompiler/tools/klayout/convert_drc_db.py +1 -1
  28. siliconcompiler/tools/klayout/drc.py +1 -1
  29. siliconcompiler/tools/klayout/export.py +8 -1
  30. siliconcompiler/tools/klayout/klayout.py +2 -2
  31. siliconcompiler/tools/klayout/klayout_convert_drc_db.py +2 -2
  32. siliconcompiler/tools/klayout/klayout_export.py +7 -5
  33. siliconcompiler/tools/klayout/klayout_operations.py +4 -3
  34. siliconcompiler/tools/klayout/klayout_show.py +3 -2
  35. siliconcompiler/tools/klayout/klayout_utils.py +1 -1
  36. siliconcompiler/tools/klayout/operations.py +8 -0
  37. siliconcompiler/tools/klayout/screenshot.py +6 -1
  38. siliconcompiler/tools/klayout/show.py +8 -1
  39. siliconcompiler/tools/magic/magic.py +1 -1
  40. siliconcompiler/tools/openroad/__init__.py +1 -1
  41. siliconcompiler/tools/openroad/_apr.py +2 -1
  42. siliconcompiler/tools/openroad/init_floorplan.py +1 -1
  43. siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +1 -1
  44. siliconcompiler/tools/openroad/scripts/apr/sc_clock_tree_synthesis.tcl +4 -0
  45. siliconcompiler/tools/openroad/scripts/apr/sc_repair_timing.tcl +2 -2
  46. siliconcompiler/tools/openroad/scripts/common/procs.tcl +52 -0
  47. siliconcompiler/tools/openroad/scripts/common/reports.tcl +1 -1
  48. siliconcompiler/tools/openroad/scripts/sc_show.tcl +5 -0
  49. siliconcompiler/tools/opensta/__init__.py +1 -1
  50. siliconcompiler/tools/opensta/check_library.py +27 -0
  51. siliconcompiler/tools/opensta/scripts/sc_check_library.tcl +255 -0
  52. siliconcompiler/tools/opensta/scripts/sc_timing.tcl +1 -1
  53. siliconcompiler/tools/sv2v/sv2v.py +1 -2
  54. siliconcompiler/tools/verilator/verilator.py +6 -7
  55. siliconcompiler/tools/vivado/vivado.py +1 -1
  56. siliconcompiler/tools/yosys/__init__.py +149 -0
  57. siliconcompiler/tools/yosys/lec.py +22 -9
  58. siliconcompiler/tools/yosys/sc_lec.tcl +94 -49
  59. siliconcompiler/tools/yosys/sc_syn.tcl +1 -0
  60. siliconcompiler/tools/yosys/screenshot.py +2 -2
  61. siliconcompiler/tools/yosys/syn_asic.py +98 -74
  62. siliconcompiler/tools/yosys/syn_asic.tcl +31 -6
  63. siliconcompiler/tools/yosys/syn_fpga.py +2 -3
  64. siliconcompiler/tools/yosys/syn_fpga.tcl +0 -1
  65. siliconcompiler/toolscripts/_tools.json +3 -3
  66. siliconcompiler/utils/__init__.py +7 -3
  67. {siliconcompiler-0.29.0.dist-info → siliconcompiler-0.29.1.dist-info}/METADATA +13 -10
  68. {siliconcompiler-0.29.0.dist-info → siliconcompiler-0.29.1.dist-info}/RECORD +72 -82
  69. {siliconcompiler-0.29.0.dist-info → siliconcompiler-0.29.1.dist-info}/WHEEL +1 -1
  70. {siliconcompiler-0.29.0.dist-info → siliconcompiler-0.29.1.dist-info}/entry_points.txt +13 -0
  71. siliconcompiler/libs/asap7sc7p5t.py +0 -8
  72. siliconcompiler/libs/gf180mcu.py +0 -8
  73. siliconcompiler/libs/interposer.py +0 -8
  74. siliconcompiler/libs/nangate45.py +0 -8
  75. siliconcompiler/libs/sg13g2_stdcell.py +0 -8
  76. siliconcompiler/libs/sky130hd.py +0 -8
  77. siliconcompiler/libs/sky130io.py +0 -8
  78. siliconcompiler/pdks/asap7.py +0 -8
  79. siliconcompiler/pdks/freepdk45.py +0 -8
  80. siliconcompiler/pdks/gf180.py +0 -8
  81. siliconcompiler/pdks/ihp130.py +0 -8
  82. siliconcompiler/pdks/interposer.py +0 -8
  83. siliconcompiler/pdks/skywater130.py +0 -8
  84. siliconcompiler/templates/replay/run.py.j2 +0 -22
  85. siliconcompiler/tools/yosys/yosys.py +0 -148
  86. {siliconcompiler-0.29.0.dist-info → siliconcompiler-0.29.1.dist-info}/LICENSE +0 -0
  87. {siliconcompiler-0.29.0.dist-info → siliconcompiler-0.29.1.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  # Version number following semver standard.
2
- version = '0.29.0'
2
+ version = '0.29.1'
3
3
 
4
4
  # Default server address for remote runs, if unspecified.
5
5
  default_server = 'https://server.siliconcompiler.com'
@@ -0,0 +1,26 @@
1
+ from siliconcompiler.apps import sc_dashboard
2
+ from siliconcompiler.apps import sc_install
3
+ from siliconcompiler.apps import sc_issue
4
+ from siliconcompiler.apps import sc_remote
5
+ from siliconcompiler.apps import sc_server
6
+ from siliconcompiler.apps import sc_show
7
+ from siliconcompiler.apps import sc
8
+ from siliconcompiler.apps import smake
9
+
10
+
11
+ def get_apps():
12
+ '''
13
+ Returns a dict of builtin apps
14
+ '''
15
+ return {
16
+ module.__name__.split(".")[-1]: module for module in (
17
+ sc_dashboard,
18
+ sc_install,
19
+ sc_issue,
20
+ sc_remote,
21
+ sc_server,
22
+ sc_show,
23
+ sc,
24
+ smake
25
+ )
26
+ }
@@ -1,9 +1,18 @@
1
1
  # Copyright 2024 Silicon Compiler Authors. All Rights Reserved.
2
2
 
3
3
  # Standard Modules
4
+ import base64
5
+ import io
6
+ import json
7
+ import gzip
4
8
  import os
5
9
  import stat
6
10
  import sys
11
+ import tarfile
12
+ import tempfile
13
+ import textwrap
14
+
15
+ from datetime import datetime
7
16
 
8
17
  import siliconcompiler
9
18
  from siliconcompiler.apps._common import UNSET_DESIGN
@@ -11,6 +20,26 @@ from siliconcompiler import SiliconCompilerError
11
20
  from siliconcompiler import utils
12
21
 
13
22
 
23
+ def make_bytes(data):
24
+ if isinstance(data, bytes):
25
+ return data
26
+ return data.encode('utf-8')
27
+
28
+
29
+ def compress(data):
30
+ return gzip.compress(make_bytes(data))
31
+
32
+
33
+ def convert_base64(data):
34
+ return base64.b64encode(make_bytes(data))
35
+
36
+
37
+ def wrap_text(data):
38
+ if isinstance(data, bytes):
39
+ data = data.decode('utf-8')
40
+ return textwrap.wrap(data)
41
+
42
+
14
43
  ###########################
15
44
  def main():
16
45
  progname = "summarize"
@@ -25,9 +54,10 @@ def main():
25
54
 
26
55
  # Read command-line inputs and generate Chip objects to run the flow on.
27
56
  try:
28
- generation_arg = {
29
- 'metavar': '<path>',
30
- 'help': '(optional) Path to generate replay files to, if specified.',
57
+ file_arg = {
58
+ 'metavar': '<file>',
59
+ 'help': 'Path to generate replay file to.',
60
+ 'default': 'replay.sh',
31
61
  'sc_print': True
32
62
  }
33
63
  args = chip.create_cmdline(
@@ -37,7 +67,7 @@ def main():
37
67
  '-jobname',
38
68
  '-loglevel'],
39
69
  additional_args={
40
- '-path': generation_arg
70
+ '-file': file_arg
41
71
  })
42
72
  except SiliconCompilerError:
43
73
  return 1
@@ -93,40 +123,68 @@ def main():
93
123
  for tool, version in tools.items():
94
124
  print(f" {os.path.basename(tool):<{tool_len}}: {', '.join(version)}")
95
125
 
96
- if 'path' in args and args['path']:
97
- path = args['path']
98
- os.makedirs(path, exist_ok=True)
99
- with open(os.path.join(path, 'requirements.txt'), 'w', encoding='utf-8') as wf:
100
- wf.write(utils.get_file_template('replay/requirements.txt').render(
101
- design=chip.design,
102
- source=', '.join(chip.find_files('option', 'cfg')),
103
- jobname=jobname,
104
- pkgs=pythonpackages
105
- ))
106
-
107
- scripts = []
108
- scripts.append(os.path.join(path, 'setup.sh'))
109
- with open(scripts[-1], 'w', encoding='utf-8') as wf:
110
- wf.write(utils.get_file_template('replay/setup.sh').render(
111
- design=chip.design,
112
- source=', '.join(chip.find_files('option', 'cfg')),
113
- jobname=jobname,
114
- pythonversion=pythonversion
115
- ))
116
-
117
- scripts.append(os.path.join(path, 'run.py'))
118
- with open(scripts[-1], 'w', encoding='utf-8') as wf:
119
- wf.write(utils.get_file_template('replay/run.py.j2').render(
120
- design=chip.design,
121
- source=', '.join(chip.find_files('option', 'cfg')),
122
- cfgs=chip.find_files('option', 'cfg'),
123
- jobname=jobname,
124
- tool_versions=sorted(tool_versions)
125
- ))
126
-
127
- for script in scripts:
128
- permissions = stat.S_IMODE(os.lstat(script).st_mode)
129
- os.chmod(script, permissions | stat.S_IXUSR)
126
+ path = os.path.abspath(args['file'])
127
+ os.makedirs(os.path.dirname(path), exist_ok=True)
128
+
129
+ starttimes = set()
130
+ for starttime, step, index in chip.schema._getvals('history', jobname, 'record', 'starttime'):
131
+ starttimes.add(datetime.strptime(starttime, '%Y-%m-%d %H:%M:%S'))
132
+ starttime = min(starttimes).strftime('%Y-%m-%d %H:%M:%S')
133
+
134
+ with io.StringIO() as fd:
135
+ fd.write(utils.get_file_template('replay/requirements.txt').render(
136
+ design=chip.design,
137
+ jobname=jobname,
138
+ date=starttime,
139
+ pkgs=pythonpackages
140
+ ))
141
+ fd.flush()
142
+ requirements_file = fd.getvalue()
143
+
144
+ with tempfile.TemporaryDirectory() as collect:
145
+ chip.collect(directory=collect, verbose=True, exclude_packages=['siliconcompiler'])
146
+
147
+ with io.BytesIO() as fd:
148
+ with tarfile.open(fileobj=fd, mode='w:gz') as tar:
149
+ tar.add(collect, arcname='')
150
+
151
+ fd.flush()
152
+ collect_files = convert_base64(fd.getvalue())
153
+
154
+ with io.StringIO() as fd:
155
+ fd.write(utils.get_file_template('replay/replay.py.j2').render(
156
+ design=chip.design,
157
+ jobname=jobname,
158
+ date=starttime,
159
+ src_file=wrap_text(collect_files),
160
+ tool_versions=sorted(tool_versions)
161
+ ))
162
+ fd.flush()
163
+ script = convert_base64(compress(fd.getvalue()))
164
+
165
+ manifest = convert_base64(compress(json.dumps(chip.schema.cfg, indent=2)))
166
+
167
+ tool_info = []
168
+ for tool, version in tools.items():
169
+ tool_info.append(f"{os.path.basename(tool):<{tool_len}}: {', '.join(version)}")
170
+
171
+ description = f"Replay for {chip.design} / {jobname}\nRun on: {starttime}"
172
+
173
+ with open(path, 'w', encoding='utf-8') as wf:
174
+ wf.write(utils.get_file_template('replay/setup.sh').render(
175
+ design=chip.design,
176
+ jobname=jobname,
177
+ date=starttime,
178
+ description=description,
179
+ pythonversion=pythonversion,
180
+ requirements=requirements_file.splitlines(),
181
+ script=wrap_text(script),
182
+ manifest=wrap_text(manifest),
183
+ tools=tool_info
184
+ ))
185
+
186
+ permissions = stat.S_IMODE(os.lstat(path).st_mode)
187
+ os.chmod(path, permissions | stat.S_IXUSR)
130
188
 
131
189
  return 0
132
190
 
@@ -0,0 +1,12 @@
1
+ from siliconcompiler.checklists import oh_tapeout
2
+
3
+
4
+ def get_checklists():
5
+ '''
6
+ Returns a dict of builtin checklists
7
+ '''
8
+ return {
9
+ module.__name__.split(".")[-1]: module for module in (
10
+ oh_tapeout,
11
+ )
12
+ }
siliconcompiler/core.py CHANGED
@@ -255,8 +255,13 @@ class Chip:
255
255
  log_format.append(f'{utils.truncate_text(step, max_step_len): <{max_step_len}}')
256
256
  log_format.append(f'{utils.truncate_text(index, max_step_len): >{max_index_len}}')
257
257
 
258
+ log_formatprefix = "| "
259
+ if loglevel == "quiet":
260
+ log_format = []
261
+ log_formatprefix = ""
262
+
258
263
  log_format.append('%(message)s')
259
- logformat = '| ' + ' | '.join(log_format)
264
+ logformat = log_formatprefix + ' | '.join(log_format)
260
265
 
261
266
  if not self.logger.hasHandlers():
262
267
  stream_handler = logging.StreamHandler(stream=sys.stdout)
@@ -402,19 +407,24 @@ class Chip:
402
407
  if extra_params is not None and "target" in extra_params:
403
408
  if extra_params["target"]:
404
409
  # running target command
405
- # Search order "{name}", and "siliconcompiler.targets.{name}"
410
+ # Search order target plugins -> "{name}"
406
411
  modules = []
407
412
  module = extra_params["target"]
408
- for mod_name in [module, f'siliconcompiler.targets.{module}']:
409
- mod = self._load_module(mod_name)
410
- if mod:
411
- modules.append(mod)
413
+ for plugin in utils.get_plugins('target'):
414
+ plugin_targets = plugin()
415
+ if module in plugin_targets:
416
+ modules.append(plugin_targets[module])
417
+
418
+ mod = self._load_module(module)
419
+ if mod:
420
+ modules.append(mod)
412
421
 
413
422
  if len(modules) == 0:
414
423
  raise SiliconCompilerError(f'Could not find target {module}', chip=self)
415
424
 
416
- self.use(modules[0])
417
- extra_params["target"] = modules[0].__name__
425
+ target = modules[0]
426
+ self.use(target)
427
+ extra_params["target"] = target.__name__
418
428
 
419
429
  if extra_params is not None and "use" in extra_params:
420
430
  if extra_params["use"]:
@@ -975,7 +985,7 @@ class Chip:
975
985
  self.logger.debug(f'Setting {keypath} to {value}')
976
986
 
977
987
  # Special case to ensure loglevel is updated ASAP
978
- if keypath == ['option', 'loglevel'] and field == 'value' and \
988
+ if tuple(keypath) == ('option', 'loglevel') and field == 'value' and \
979
989
  step == self.get('arg', 'step') and index == self.get('arg', 'index'):
980
990
  self.logger.setLevel(schema_utils.translate_loglevel(value))
981
991
 
@@ -1074,6 +1084,54 @@ class Chip:
1074
1084
  except (ValueError, TypeError) as e:
1075
1085
  self.error(str(e))
1076
1086
 
1087
+ def import_flist(self, filename, package=None):
1088
+ '''
1089
+ Add input files, include directories, and defines from an flist
1090
+
1091
+ Args:
1092
+ filename (path): Path to flist file
1093
+ package (str): name of package
1094
+ '''
1095
+
1096
+ if package:
1097
+ filename = os.path.join(sc_package.path(self, package), filename)
1098
+
1099
+ if not os.path.isfile(filename):
1100
+ raise FileNotFoundError(filename)
1101
+
1102
+ package_name = f'flist-{os.path.basename(filename)}'
1103
+ package_dir = os.path.dirname(os.path.abspath(filename))
1104
+
1105
+ def __make_path(rel, path):
1106
+ path = utils._resolve_env_vars(self, path)
1107
+ if os.path.isabs(path):
1108
+ if path.startswith(rel):
1109
+ return os.path.relpath(path, rel), package_name
1110
+ else:
1111
+ return path, None
1112
+ return path, package_name
1113
+
1114
+ self.register_source(
1115
+ package_name,
1116
+ path=package_dir)
1117
+ with utils.sc_open(filename) as f:
1118
+ for line in f:
1119
+ line = line.strip()
1120
+ if not line:
1121
+ continue
1122
+ if line.startswith("//"):
1123
+ continue
1124
+ if line.startswith("+incdir+"):
1125
+ line = line[8:]
1126
+ path, package = __make_path(package_dir, line)
1127
+ self.add('option', 'idir', path, package=package)
1128
+ elif line.startswith("+define+"):
1129
+ line = line[8:]
1130
+ self.add('option', 'define', line)
1131
+ else:
1132
+ path, package = __make_path(package_dir, line)
1133
+ self.input(path, package=package)
1134
+
1077
1135
  ###########################################################################
1078
1136
  def input(self, filename, fileset=None, filetype=None, iomap=None,
1079
1137
  step=None, index=None, package=None):
@@ -2384,7 +2442,7 @@ class Chip:
2384
2442
  swap('library', lib, 'option', 'library')
2385
2443
 
2386
2444
  ########################################################################
2387
- def collect(self, directory=None, verbose=True, whitelist=None):
2445
+ def collect(self, directory=None, verbose=True, whitelist=None, exclude_packages=None):
2388
2446
  '''
2389
2447
  Collects files found in the configuration dictionary and places
2390
2448
  them in inputs/. The function only copies in files that have the 'copy'
@@ -2402,6 +2460,8 @@ class Chip:
2402
2460
  whitelist (list[path]): List of directories that are allowed to be
2403
2461
  collected. If a directory is is found that is not on this list
2404
2462
  a RuntimeError will be raised.
2463
+ package_filter (list[str]): List of packages to exclude from
2464
+ collection.
2405
2465
  '''
2406
2466
 
2407
2467
  if not directory:
@@ -2414,6 +2474,9 @@ class Chip:
2414
2474
  if verbose:
2415
2475
  self.logger.info('Collecting input sources')
2416
2476
 
2477
+ if not exclude_packages:
2478
+ exclude_packages = []
2479
+
2417
2480
  dirs = {}
2418
2481
  files = {}
2419
2482
 
@@ -2453,6 +2516,8 @@ class Chip:
2453
2516
  if not package:
2454
2517
  # Ensure package is an empty string
2455
2518
  package = ''
2519
+ if package in exclude_packages:
2520
+ continue
2456
2521
  if is_dir:
2457
2522
  dirs[(package, path)] = abspath
2458
2523
  else:
@@ -0,0 +1,34 @@
1
+ from siliconcompiler.flows import asicflow
2
+ from siliconcompiler.flows import asictopflow
3
+ from siliconcompiler.flows import drcflow
4
+ from siliconcompiler.flows import dvflow
5
+ from siliconcompiler.flows import fpgaflow
6
+ from siliconcompiler.flows import generate_openroad_rcx
7
+ from siliconcompiler.flows import interposerflow
8
+ from siliconcompiler.flows import lintflow
9
+ from siliconcompiler.flows import screenshotflow
10
+ from siliconcompiler.flows import showflow
11
+ from siliconcompiler.flows import signoffflow
12
+ from siliconcompiler.flows import synflow
13
+
14
+
15
+ def get_flows():
16
+ '''
17
+ Returns a dict of builtin flows
18
+ '''
19
+ return {
20
+ module.__name__.split(".")[-1]: module for module in (
21
+ asicflow,
22
+ asictopflow,
23
+ drcflow,
24
+ dvflow,
25
+ fpgaflow,
26
+ generate_openroad_rcx,
27
+ interposerflow,
28
+ lintflow,
29
+ screenshotflow,
30
+ showflow,
31
+ signoffflow,
32
+ synflow
33
+ )
34
+ }
@@ -1,12 +1,12 @@
1
1
  import siliconcompiler
2
2
  from siliconcompiler import SiliconCompilerError
3
- from siliconcompiler.targets import freepdk45_demo
4
3
 
5
4
 
6
5
  ############################################################################
7
6
  # DOCS
8
7
  ############################################################################
9
8
  def make_docs(chip):
9
+ from siliconcompiler.targets import freepdk45_demo
10
10
  chip.use(freepdk45_demo)
11
11
  return setup(filetype='gds', showtools=chip._showtools, np=3)
12
12
 
@@ -0,0 +1,5 @@
1
+ def get_libs():
2
+ '''
3
+ Returns a dict of builtin libraries
4
+ '''
5
+ return {}
@@ -0,0 +1,199 @@
1
+ class Optimizer:
2
+ def __init__(self, chip):
3
+ self._chip = chip
4
+
5
+ self._parameters = {}
6
+ self._goals = {}
7
+ self._assertions = {}
8
+
9
+ self.__results = []
10
+
11
+ def __generate_print_name(self, key, step, index):
12
+ name = f'[{",".join(key)}]'
13
+
14
+ node_name = None
15
+ if step is not None:
16
+ node_name = step
17
+
18
+ if index is not None:
19
+ node_name += f'{index}'
20
+
21
+ if node_name:
22
+ name += f' ({node_name})'
23
+
24
+ return name
25
+
26
+ def __generate_param_name(self, key, step, index):
27
+ name = ",".join(key)
28
+
29
+ if step is not None:
30
+ name += f'-step-{step}'
31
+
32
+ if index is not None:
33
+ name += f'-index-{index}'
34
+
35
+ return name
36
+
37
+ def add_parameter(self, key, values, value_type=None, step=None, index=None):
38
+ if value_type is None:
39
+ value_type = self._chip.get(*key, field='type')
40
+ if value_type.startswith('['):
41
+ value_type = value_type[1:-1]
42
+ elif value_type.startswith('('):
43
+ value_type = value_type[1:-1].split(",")
44
+ value_type = [value.strip() for value in value_type]
45
+ if not all([value == value_type[0] for value in value_type]):
46
+ raise ValueError("Cannot support unequal tuples")
47
+ value_type = value_type[0]
48
+
49
+ if value_type not in ('float', 'int', 'bool', 'enum', 'str'):
50
+ raise ValueError(f"{value_type} is not supported")
51
+
52
+ if value_type in ('float', 'int'):
53
+ if 'max' not in values:
54
+ raise ValueError("value must have a max key")
55
+ if 'min' not in values:
56
+ raise ValueError("value must have a min key")
57
+ values = [values["min"], values["max"]]
58
+ elif value_type in ('enum', 'str', 'bool'):
59
+ if not isinstance(values, (tuple, list, set)):
60
+ raise ValueError("value must be a list")
61
+ if value_type == 'str':
62
+ value_type = 'enum'
63
+
64
+ if value_type == 'bool' and not values:
65
+ values = [True, False]
66
+
67
+ self._parameters["param-" + self.__generate_param_name(key, step, index)] = {
68
+ "print": self.__generate_print_name(key, step, index),
69
+ "key": tuple(key),
70
+ "type": value_type,
71
+ "values": tuple(values),
72
+ "step": step,
73
+ "index": index
74
+ }
75
+
76
+ def _set_parameter(self, parameter, value, chip, flow_prefix=None):
77
+ param_entry = self._parameters[parameter]
78
+
79
+ self._chip.logger.info(f' Setting {param_entry["print"]} = {value}')
80
+ if param_entry["step"]:
81
+ if not flow_prefix:
82
+ flow_prefix = ""
83
+ step = f'{flow_prefix}{param_entry["step"]}'
84
+ else:
85
+ step = param_entry["step"]
86
+
87
+ key_type = chip.get(*param_entry["key"], field='type')
88
+ if key_type[0] == "(":
89
+ key_type = key_type[1:-1].split(",")
90
+ value = len(key_type) * [value]
91
+
92
+ chip.set(
93
+ *param_entry["key"],
94
+ value,
95
+ step=step,
96
+ index=param_entry["index"])
97
+
98
+ def add_assertion(self, key, criteria, step=None, index=None):
99
+ if not callable(criteria):
100
+ raise ValueError('criteria must be a function')
101
+
102
+ if not step:
103
+ raise ValueError('step is required')
104
+
105
+ if not index:
106
+ raise ValueError('index is required')
107
+
108
+ self._assertions["assert-" + self.__generate_param_name(key, step, index)] = {
109
+ "print": self.__generate_print_name(key, step, index),
110
+ "key": tuple(key),
111
+ "criteria": criteria,
112
+ "step": step,
113
+ "index": index
114
+ }
115
+
116
+ def _check_assertions(self, chip, step_prefix):
117
+ if not step_prefix:
118
+ step_prefix = ""
119
+
120
+ for info in self._assertions.values():
121
+ value = chip.get(
122
+ *info["key"],
123
+ step=f'{step_prefix}{info["step"]}',
124
+ index=info["index"])
125
+ if not info["criteria"](value):
126
+ self._chip.logger.error(f"Failed to meet assertion: {info['print']} with {value}")
127
+ return False
128
+
129
+ return True
130
+
131
+ def add_goal(self, key, goal, stop_goal=None, step=None, index=None):
132
+ if goal not in ('min', 'max'):
133
+ raise ValueError(f"{goal} is not supported")
134
+
135
+ if not step:
136
+ raise ValueError('step is required')
137
+
138
+ if not index:
139
+ raise ValueError('index is required')
140
+
141
+ self._goals["goal-" + self.__generate_param_name(key, step, index)] = {
142
+ "print": self.__generate_print_name(key, step, index),
143
+ "key": tuple(key),
144
+ "goal": goal,
145
+ "stop": stop_goal,
146
+ "step": step,
147
+ "index": index
148
+ }
149
+
150
+ def _check_stop_goal(self, measurements):
151
+ cont = []
152
+
153
+ for param, info in self._goals.items():
154
+ if info["stop"] is None:
155
+ continue
156
+
157
+ if param not in measurements:
158
+ cont.append(False)
159
+ continue
160
+
161
+ if info["goal"] == "min":
162
+ if measurements[param] <= info["stop"]:
163
+ cont.append(True)
164
+ elif info["goal"] == "max":
165
+ if measurements[param] >= info["stop"]:
166
+ cont.append(True)
167
+
168
+ if not cont:
169
+ return False
170
+
171
+ return all(cont)
172
+
173
+ def run(self, experiments=None, parallel=None):
174
+ raise NotImplementedError
175
+
176
+ def _clear_results(self):
177
+ self.__results.clear()
178
+
179
+ def _add_result(self, parameters, measurements):
180
+ self.__results.append({
181
+ "parameters": parameters,
182
+ "measurements": measurements
183
+ })
184
+
185
+ def report(self, count=None):
186
+ for n, result in enumerate(self.__results):
187
+ if count and n >= count:
188
+ return
189
+
190
+ self._chip.logger.info(f"Result {n+1} / {len(self.__results)}:")
191
+ self._chip.logger.info(" Parameters:")
192
+ for param_name, param_key in result["parameters"].items():
193
+ param_print = self._parameters[param_name]['print']
194
+ self._chip.logger.info(f" {param_print} = {param_key}")
195
+
196
+ self._chip.logger.info(" Measurements:")
197
+ for meas_name, meas_key in result["measurements"].metrics.items():
198
+ goal_print = self._goals[meas_name]['print']
199
+ self._chip.logger.info(f" {goal_print} = {meas_key.value}")