siliconcompiler 0.29.4__py3-none-any.whl → 0.31.0__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 (84) hide show
  1. siliconcompiler/_metadata.py +1 -1
  2. siliconcompiler/apps/sc.py +1 -1
  3. siliconcompiler/apps/sc_install.py +35 -3
  4. siliconcompiler/apps/sc_remote.py +1 -3
  5. siliconcompiler/core.py +38 -12
  6. siliconcompiler/flowgraph.py +11 -23
  7. siliconcompiler/package.py +1 -1
  8. siliconcompiler/remote/schema.py +9 -8
  9. siliconcompiler/report/report.py +4 -3
  10. siliconcompiler/scheduler/__init__.py +109 -104
  11. siliconcompiler/scheduler/docker_runner.py +1 -1
  12. siliconcompiler/scheduler/send_messages.py +1 -1
  13. siliconcompiler/schema/schema_cfg.py +478 -411
  14. siliconcompiler/schema/schema_obj.py +32 -18
  15. siliconcompiler/schema/utils.py +19 -0
  16. siliconcompiler/sphinx_ext/schemagen.py +3 -1
  17. siliconcompiler/templates/replay/replay.sh.j2 +92 -0
  18. siliconcompiler/tools/__init__.py +3 -1
  19. siliconcompiler/tools/_common/__init__.py +8 -2
  20. siliconcompiler/tools/_common/asic.py +1 -1
  21. siliconcompiler/tools/bluespec/__init__.py +35 -0
  22. siliconcompiler/tools/bluespec/convert.py +44 -5
  23. siliconcompiler/tools/graphviz/__init__.py +12 -0
  24. siliconcompiler/tools/graphviz/screenshot.py +48 -0
  25. siliconcompiler/tools/graphviz/show.py +20 -0
  26. siliconcompiler/tools/klayout/export.py +5 -0
  27. siliconcompiler/tools/klayout/klayout.py +18 -1
  28. siliconcompiler/tools/klayout/klayout_export.py +4 -1
  29. siliconcompiler/tools/klayout/klayout_operations.py +5 -2
  30. siliconcompiler/tools/klayout/klayout_utils.py +23 -0
  31. siliconcompiler/tools/klayout/operations.py +5 -0
  32. siliconcompiler/tools/magic/magic.py +1 -1
  33. siliconcompiler/tools/openroad/_apr.py +20 -3
  34. siliconcompiler/tools/openroad/antenna_repair.py +2 -1
  35. siliconcompiler/tools/openroad/clock_tree_synthesis.py +2 -1
  36. siliconcompiler/tools/openroad/detailed_placement.py +2 -1
  37. siliconcompiler/tools/openroad/detailed_route.py +8 -0
  38. siliconcompiler/tools/openroad/fillercell_insertion.py +2 -1
  39. siliconcompiler/tools/openroad/global_placement.py +2 -1
  40. siliconcompiler/tools/openroad/macro_placement.py +9 -0
  41. siliconcompiler/tools/openroad/pin_placement.py +2 -1
  42. siliconcompiler/tools/openroad/power_grid.py +6 -0
  43. siliconcompiler/tools/openroad/repair_design.py +2 -1
  44. siliconcompiler/tools/openroad/repair_timing.py +2 -1
  45. siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +6 -0
  46. siliconcompiler/tools/openroad/scripts/apr/sc_clock_tree_synthesis.tcl +3 -0
  47. siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +10 -0
  48. siliconcompiler/tools/openroad/scripts/apr/sc_global_placement.tcl +1 -0
  49. siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +17 -5
  50. siliconcompiler/tools/openroad/scripts/apr/sc_macro_placement.tcl +14 -1
  51. siliconcompiler/tools/openroad/scripts/apr/sc_power_grid.tcl +54 -0
  52. siliconcompiler/tools/openroad/scripts/apr/sc_repair_design.tcl +1 -0
  53. siliconcompiler/tools/openroad/scripts/apr/sc_repair_timing.tcl +3 -0
  54. siliconcompiler/tools/openroad/scripts/common/procs.tcl +55 -17
  55. siliconcompiler/tools/openroad/scripts/common/reports.tcl +25 -3
  56. siliconcompiler/tools/openroad/scripts/common/write_images.tcl +28 -0
  57. siliconcompiler/tools/yosys/__init__.py +7 -0
  58. siliconcompiler/tools/yosys/sc_syn.tcl +33 -24
  59. siliconcompiler/tools/yosys/syn_asic.py +27 -0
  60. siliconcompiler/tools/yosys/syn_asic.tcl +27 -0
  61. siliconcompiler/toolscripts/_tools.json +16 -4
  62. siliconcompiler/toolscripts/rhel8/install-yosys-moosic.sh +17 -0
  63. siliconcompiler/toolscripts/rhel8/install-yosys-slang.sh +22 -0
  64. siliconcompiler/toolscripts/rhel9/install-openroad.sh +1 -1
  65. siliconcompiler/toolscripts/rhel9/install-yosys-moosic.sh +17 -0
  66. siliconcompiler/toolscripts/rhel9/install-yosys-slang.sh +22 -0
  67. siliconcompiler/toolscripts/ubuntu20/install-openroad.sh +1 -1
  68. siliconcompiler/toolscripts/ubuntu20/install-yosys-moosic.sh +17 -0
  69. siliconcompiler/toolscripts/ubuntu20/install-yosys-slang.sh +22 -0
  70. siliconcompiler/toolscripts/ubuntu22/install-openroad.sh +1 -1
  71. siliconcompiler/toolscripts/ubuntu22/install-yosys-moosic.sh +17 -0
  72. siliconcompiler/toolscripts/ubuntu22/install-yosys-slang.sh +22 -0
  73. siliconcompiler/toolscripts/ubuntu24/install-openroad.sh +1 -1
  74. siliconcompiler/toolscripts/ubuntu24/install-yosys-moosic.sh +17 -0
  75. siliconcompiler/toolscripts/ubuntu24/install-yosys-slang.sh +22 -0
  76. siliconcompiler/utils/__init__.py +44 -5
  77. siliconcompiler/utils/showtools.py +7 -0
  78. {siliconcompiler-0.29.4.dist-info → siliconcompiler-0.31.0.dist-info}/METADATA +8 -8
  79. {siliconcompiler-0.29.4.dist-info → siliconcompiler-0.31.0.dist-info}/RECORD +83 -70
  80. {siliconcompiler-0.29.4.dist-info → siliconcompiler-0.31.0.dist-info}/WHEEL +1 -1
  81. siliconcompiler/tools/bluespec/bluespec.py +0 -40
  82. {siliconcompiler-0.29.4.dist-info → siliconcompiler-0.31.0.dist-info}/LICENSE +0 -0
  83. {siliconcompiler-0.29.4.dist-info → siliconcompiler-0.31.0.dist-info}/entry_points.txt +0 -0
  84. {siliconcompiler-0.29.4.dist-info → siliconcompiler-0.31.0.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  # Version number following semver standard.
2
- version = '0.29.4'
2
+ version = '0.31.0'
3
3
 
4
4
  # Default server address for remote runs, if unspecified.
5
5
  default_server = 'https://server.siliconcompiler.com'
@@ -95,7 +95,7 @@ def main():
95
95
 
96
96
  try:
97
97
  # Run flow
98
- chip.run()
98
+ chip.run(raise_exception=True)
99
99
 
100
100
  # Print Job Summary
101
101
  chip.summary()
@@ -17,7 +17,7 @@ class ChoiceOptional(Container):
17
17
  def __init__(self, choices):
18
18
  super().__init__()
19
19
 
20
- self.__choices = set(choices)
20
+ self.__choices = sorted(set(choices))
21
21
 
22
22
  def __contains__(self, item):
23
23
  if not item:
@@ -30,7 +30,7 @@ class ChoiceOptional(Container):
30
30
 
31
31
  def get_items(self, choices):
32
32
  items = set(choices)
33
- return sorted(list(items))
33
+ return sorted(items)
34
34
 
35
35
 
36
36
  def install_tool(tool, script, build_dir, prefix):
@@ -143,6 +143,10 @@ def _recommended_tool_groups(tools):
143
143
  return filter_groups
144
144
 
145
145
 
146
+ class HelpFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter):
147
+ pass
148
+
149
+
146
150
  def main():
147
151
  progname = "sc-install"
148
152
  description = """
@@ -174,7 +178,7 @@ To system debugging information (this should only be used to debug):
174
178
  parser = argparse.ArgumentParser(
175
179
  prog=progname,
176
180
  description=description,
177
- formatter_class=argparse.ArgumentDefaultsHelpFormatter)
181
+ formatter_class=HelpFormatter)
178
182
 
179
183
  tools = _get_tools_list()
180
184
 
@@ -248,6 +252,34 @@ To system debugging information (this should only be used to debug):
248
252
  if not install_tool(tool, tools[tool], args.build_dir, args.prefix):
249
253
  return 1
250
254
 
255
+ if not args.show:
256
+ msgs = []
257
+ for env, path in (
258
+ ("PATH", "bin"),
259
+ ("LD_LIBRARY_PATH", "lib")):
260
+ check_path = os.path.join(args.prefix, path)
261
+ envs = [
262
+ os.path.expandvars(os.path.expanduser(p))
263
+ for p in os.getenv(env, "").split(":")
264
+ ]
265
+ print(envs)
266
+ if check_path not in envs:
267
+ msgs.extend([
268
+ "",
269
+ f"{check_path} not found in {env}",
270
+ "you may need to add it the following your shell",
271
+ "initialization script:",
272
+ f'export {env}="{check_path}:${env}"'
273
+ ])
274
+ if msgs:
275
+ center_len = max(len(msg) for msg in msgs)
276
+ max_len = center_len + 4
277
+ print("#"*max_len)
278
+ for msg in msgs:
279
+ print(f"# {msg:<{center_len}} #")
280
+ print(f"# {' '*center_len} #")
281
+ print("#"*max_len)
282
+
251
283
  return 0
252
284
 
253
285
 
@@ -1,5 +1,4 @@
1
1
  # Copyright 2023 Silicon Compiler Authors. All Rights Reserved.
2
- import copy
3
2
  import os
4
3
  import sys
5
4
 
@@ -157,7 +156,6 @@ To delete a job, use:
157
156
  # in its "check_progress/ until job is done" loop.
158
157
  elif args['reconnect']:
159
158
  # Start from successors of entry nodes, so entry nodes are not fetched from remote.
160
- environment = copy.deepcopy(os.environ)
161
159
  flow = chip.get('option', 'flow')
162
160
  entry_nodes = _get_flowgraph_entry_nodes(chip, flow)
163
161
  for entry_node in entry_nodes:
@@ -177,7 +175,7 @@ To delete a job, use:
177
175
  f'{chip.design}.pkg.json')
178
176
  if os.path.exists(manifest):
179
177
  chip.schema.read_journal(manifest)
180
- _finalize_run(chip, environment)
178
+ _finalize_run(chip)
181
179
 
182
180
  # Summarize the run.
183
181
  chip.summary()
siliconcompiler/core.py CHANGED
@@ -67,7 +67,7 @@ class Chip:
67
67
  except FileNotFoundError:
68
68
  raise SiliconCompilerError(
69
69
  "SiliconCompiler must be run from a directory that exists. "
70
- "If you are sure that your working directory is valid, try running `cd $(pwd)`.""")
70
+ "If you are sure that your working directory is valid, try running `cd $(pwd)`.")
71
71
 
72
72
  # Initialize custom error handling for codecs. This has to be called
73
73
  # by each spawned (as opposed to forked) subprocess
@@ -392,7 +392,7 @@ class Chip:
392
392
  for vals, step, index in self.schema._getvals(*key):
393
393
  if not vals:
394
394
  continue
395
- if self.get(*key, field='pernode') != 'never':
395
+ if not self.get(*key, field='pernode').is_never():
396
396
  if step is None:
397
397
  step = Schema.GLOBAL_KEY
398
398
  if index is None:
@@ -852,7 +852,7 @@ class Chip:
852
852
  strict = self.schema.get('option', 'strict')
853
853
  if field == 'value' and strict:
854
854
  pernode = self.schema.get(*keypath, field='pernode')
855
- if pernode == 'optional' and \
855
+ if pernode == schema_utils.PerNode.OPTIONAL and \
856
856
  (step is None or index is None) and \
857
857
  (Schema.GLOBAL_KEY not in (step, index)): # allow explicit access to global
858
858
  self.error(
@@ -1111,7 +1111,7 @@ class Chip:
1111
1111
  package_dir = os.path.dirname(os.path.abspath(filename))
1112
1112
 
1113
1113
  def __make_path(rel, path):
1114
- path = utils._resolve_env_vars(self, path)
1114
+ path = utils._resolve_env_vars(self, path, None, None)
1115
1115
  if os.path.isabs(path):
1116
1116
  if path.startswith(rel):
1117
1117
  return os.path.relpath(path, rel), package_name
@@ -1282,7 +1282,7 @@ class Chip:
1282
1282
  """
1283
1283
  strict = self.get('option', 'strict')
1284
1284
  pernode = self.get(*keypath, field='pernode')
1285
- if strict and pernode == 'optional' and (step is None or index is None):
1285
+ if strict and pernode == schema_utils.PerNode.OPTIONAL and (step is None or index is None):
1286
1286
  self.error(
1287
1287
  f"Invalid args to find_files() of keypath {keypath}: step and "
1288
1288
  "index are required for reading from this parameter while "
@@ -1394,7 +1394,9 @@ class Chip:
1394
1394
  result.append(utils.find_sc_file(self,
1395
1395
  path,
1396
1396
  missing_ok=missing_ok,
1397
- search_paths=search_paths))
1397
+ search_paths=search_paths,
1398
+ step=step,
1399
+ index=index))
1398
1400
 
1399
1401
  if self._relative_path and not abs_path_only:
1400
1402
  rel_result = []
@@ -2828,10 +2830,10 @@ class Chip:
2828
2830
  set_step = None
2829
2831
  set_index = None
2830
2832
  pernode = self.get(*keypath, field='pernode')
2831
- if pernode == 'required':
2833
+ if pernode == schema_utils.PerNode.REQUIRED:
2832
2834
  set_step = step
2833
2835
  set_index = index
2834
- elif pernode == 'optional':
2836
+ elif pernode == schema_utils.PerNode.OPTIONAL:
2835
2837
  for vals, key_step, key_index in self.schema._getvals(*keypath):
2836
2838
  if key_step == step and key_index == index and vals:
2837
2839
  set_step = step
@@ -2887,7 +2889,7 @@ class Chip:
2887
2889
  return self._dash
2888
2890
 
2889
2891
  ###########################################################################
2890
- def summary(self, show_all_indices=False, generate_image=True, generate_html=True):
2892
+ def summary(self, show_all_indices=False, generate_image=True, generate_html=False):
2891
2893
  '''
2892
2894
  Prints a summary of the compilation manifest.
2893
2895
 
@@ -2927,6 +2929,10 @@ class Chip:
2927
2929
  results_img = os.path.join(work_dir, f'{self.design}.png')
2928
2930
  results_html = os.path.join(work_dir, 'report.html')
2929
2931
 
2932
+ for path in (results_img, results_html):
2933
+ if os.path.exists(path):
2934
+ os.remove(path)
2935
+
2930
2936
  if generate_image:
2931
2937
  _generate_summary_image(self, results_img)
2932
2938
 
@@ -3092,6 +3098,13 @@ class Chip:
3092
3098
  step (str): Step name
3093
3099
  index (int): Step index
3094
3100
  '''
3101
+
3102
+ if flow not in self.getkeys('flowgraph'):
3103
+ raise ValueError(f'{flow} is not in the manifest')
3104
+
3105
+ if step not in self.getkeys('flowgraph', flow):
3106
+ raise ValueError(f'{step} is not a valid step in {flow}')
3107
+
3095
3108
  if index is None:
3096
3109
  # Iterate over all indexes
3097
3110
  for index in self.getkeys('flowgraph', flow, step):
@@ -3099,6 +3112,8 @@ class Chip:
3099
3112
  return
3100
3113
 
3101
3114
  index = str(index)
3115
+ if index not in self.getkeys('flowgraph', flow, step):
3116
+ raise ValueError(f'{index} is not a valid index for {step} in {flow}')
3102
3117
 
3103
3118
  # Save input edges
3104
3119
  node = (step, index)
@@ -3153,7 +3168,7 @@ class Chip:
3153
3168
  self.add('flowgraph', flow, newstep, index, 'input', (newin, in_index))
3154
3169
 
3155
3170
  ###########################################################################
3156
- def run(self):
3171
+ def run(self, raise_exception=False):
3157
3172
  '''
3158
3173
  Executes tasks in a flowgraph.
3159
3174
 
@@ -3177,12 +3192,23 @@ class Chip:
3177
3192
  processes to exit before start, returning control to the the main
3178
3193
  program which can then exit.
3179
3194
 
3195
+ Args:
3196
+ raise_exception (bool): if True, will rethrow errors that the flow raises,
3197
+ otherwise will report the error and return False
3198
+
3180
3199
  Examples:
3181
3200
  >>> run()
3182
3201
  Runs the execution flow defined by the flowgraph dictionary.
3183
3202
  '''
3184
3203
 
3185
- sc_runner(self)
3204
+ try:
3205
+ sc_runner(self)
3206
+ except Exception as e:
3207
+ if raise_exception:
3208
+ raise e
3209
+ self.logger.error(str(e))
3210
+ return False
3211
+ return True
3186
3212
 
3187
3213
  ###########################################################################
3188
3214
  def show(self, filename=None, screenshot=False, extension=None):
@@ -3320,7 +3346,7 @@ class Chip:
3320
3346
 
3321
3347
  # run show flow
3322
3348
  try:
3323
- self.run()
3349
+ self.run(raise_exception=True)
3324
3350
  if screenshot:
3325
3351
  step, index = _get_flowgraph_exit_nodes(self, flow='showflow')[0]
3326
3352
  success = self.find_result('png', step=step, index=index)
@@ -4,23 +4,6 @@ from siliconcompiler import SiliconCompilerError, NodeStatus
4
4
  from siliconcompiler.tools._common import input_file_node_name, get_tool_task
5
5
 
6
6
 
7
- def _check_execution_nodes_inputs(chip, flow):
8
- for node in nodes_to_execute(chip, flow):
9
- if node in _get_execution_entry_nodes(chip, flow):
10
- continue
11
- pruned_node_inputs = set(_get_pruned_node_inputs(chip, flow, node))
12
- node_inputs = set(_get_flowgraph_node_inputs(chip, flow, node))
13
- tool, task = get_tool_task(chip, node[0], node[1], flow=flow)
14
- if tool == 'builtin' and not pruned_node_inputs or \
15
- tool != 'builtin' and pruned_node_inputs != node_inputs:
16
- chip.logger.warning(
17
- f'Flowgraph connection from {node_inputs.difference(pruned_node_inputs)} '
18
- f'to {node} is missing. '
19
- f'Double check your flowgraph and from/to/prune options.')
20
- return False
21
- return True
22
-
23
-
24
7
  def _nodes_to_execute(chip, flow, from_nodes, to_nodes, prune_nodes):
25
8
  '''
26
9
  Assumes a flowgraph with valid edges for the inputs
@@ -283,12 +266,12 @@ def nodes_to_execute(chip, flow=None):
283
266
  if flow is None:
284
267
  flow = chip.get('option', 'flow')
285
268
 
286
- from_nodes = _get_execution_entry_nodes(chip, flow)
287
- to_nodes = _get_execution_exit_nodes(chip, flow)
288
- prune_nodes = chip.get('option', 'prune')
269
+ from_nodes = set(_get_execution_entry_nodes(chip, flow))
270
+ to_nodes = set(_get_execution_exit_nodes(chip, flow))
271
+ prune_nodes = set(chip.get('option', 'prune'))
289
272
  if from_nodes == to_nodes:
290
273
  return list(filter(lambda node: node not in prune_nodes, from_nodes))
291
- return _nodes_to_execute(chip, flow, set(from_nodes), set(to_nodes), set(prune_nodes))
274
+ return _nodes_to_execute(chip, flow, from_nodes, to_nodes, prune_nodes)
292
275
 
293
276
 
294
277
  ###########################################################################
@@ -345,8 +328,13 @@ def _check_flowgraph(chip, flow=None):
345
328
  chip.logger.error(f'{step} is not defined in the {flow} flowgraph')
346
329
  error = True
347
330
 
348
- if not _check_execution_nodes_inputs(chip, flow):
349
- error = True
331
+ for step, index in chip.get('option', 'prune'):
332
+ if step not in chip.getkeys('flowgraph', flow):
333
+ chip.logger.error(f'{step} is not defined in the {flow} flowgraph')
334
+ error = True
335
+ elif str(index) not in chip.getkeys('flowgraph', flow, step):
336
+ chip.logger.error(f'{step}{index} is not defined in the {flow} flowgraph')
337
+ error = True
350
338
 
351
339
  unreachable_steps = _unreachable_steps_to_execute(chip, flow)
352
340
  if unreachable_steps:
@@ -46,7 +46,7 @@ def _path(chip, package, download_handler):
46
46
  f'Could not find package source for {package} in schema. '
47
47
  'You can use register_source() to add it.', chip=chip)
48
48
 
49
- data['path'] = _resolve_env_vars(chip, data['path'])
49
+ data['path'] = _resolve_env_vars(chip, data['path'], None, None)
50
50
 
51
51
  url = urlparse(data['path'])
52
52
 
@@ -1,5 +1,6 @@
1
1
  from siliconcompiler.schema.schema_cfg import scparam
2
2
  from siliconcompiler.schema import Schema
3
+ from siliconcompiler.schema.utils import PerNode, Scope
3
4
 
4
5
 
5
6
  SCHEMA_VERSION = '0.0.2'
@@ -11,7 +12,7 @@ def schema_cfg():
11
12
 
12
13
  scparam(cfg, ['schemaversion'],
13
14
  sctype='str',
14
- scope='global',
15
+ scope=Scope.GLOBAL,
15
16
  defvalue=SCHEMA_VERSION,
16
17
  require='all',
17
18
  shorthelp="Schema version number",
@@ -22,7 +23,7 @@ def schema_cfg():
22
23
 
23
24
  scparam(cfg, ['option', 'port'],
24
25
  sctype='int',
25
- scope='global',
26
+ scope=Scope.GLOBAL,
26
27
  defvalue=8080,
27
28
  require='all',
28
29
  shorthelp="Port number to run the server on.",
@@ -34,7 +35,7 @@ def schema_cfg():
34
35
  scparam(cfg, ['option', 'cluster'],
35
36
  sctype='enum',
36
37
  enum=['local', 'slurm'],
37
- scope='global',
38
+ scope=Scope.GLOBAL,
38
39
  defvalue='local',
39
40
  require='all',
40
41
  shorthelp="Type of compute cluster to use.",
@@ -45,7 +46,7 @@ def schema_cfg():
45
46
 
46
47
  scparam(cfg, ['option', 'nfsmount'],
47
48
  sctype='dir',
48
- scope='global',
49
+ scope=Scope.GLOBAL,
49
50
  defvalue='/nfs/sc_compute',
50
51
  require='all',
51
52
  shorthelp="Directory of mounted shared NFS storage.",
@@ -56,7 +57,7 @@ def schema_cfg():
56
57
 
57
58
  scparam(cfg, ['option', 'auth'],
58
59
  sctype='bool',
59
- scope='global',
60
+ scope=Scope.GLOBAL,
60
61
  defvalue=False,
61
62
  require='all',
62
63
  shorthelp="Flag determining whether to enable authenticated and encrypted jobs.",
@@ -67,7 +68,7 @@ def schema_cfg():
67
68
 
68
69
  scparam(cfg, ['option', 'cfg'],
69
70
  sctype='[file]',
70
- scope='job',
71
+ scope=Scope.JOB,
71
72
  shorthelp="Configuration manifest",
72
73
  switch="-cfg <file>",
73
74
  example=["cli: -cfg mypdk.json",
@@ -82,8 +83,8 @@ def schema_cfg():
82
83
  scparam(cfg, ['option', 'loglevel'],
83
84
  sctype='enum',
84
85
  enum=["info", "warning", "error", "critical", "debug"],
85
- pernode='optional',
86
- scope='job',
86
+ pernode=PerNode.OPTIONAL,
87
+ scope=Scope.JOB,
87
88
  defvalue='info',
88
89
  shorthelp="Logging level",
89
90
  switch="-loglevel <str>",
@@ -2,6 +2,7 @@ import fnmatch
2
2
  import pandas
3
3
  import os
4
4
  from siliconcompiler import Schema
5
+ from siliconcompiler.schema.utils import PerNode
5
6
  from siliconcompiler.report import utils
6
7
  from siliconcompiler.flowgraph import nodes_to_execute
7
8
  from siliconcompiler.tools._common import get_tool_task
@@ -53,7 +54,7 @@ def get_flowgraph_nodes(chip, step, index):
53
54
  if task is not None:
54
55
  nodes['task'] = task
55
56
  for key in chip.getkeys('record'):
56
- if chip.get('record', key, field='pernode') == 'never':
57
+ if chip.get('record', key, field='pernode').is_never():
57
58
  value = chip.get('record', key)
58
59
  else:
59
60
  value = chip.get('record', key, step=step, index=index)
@@ -106,7 +107,7 @@ def make_manifest_helper(manifest_subsect, modified_manifest_subsect):
106
107
  '''
107
108
 
108
109
  def build_leaf(manifest_subsect):
109
- if manifest_subsect['pernode'] == 'never':
110
+ if PerNode(manifest_subsect['pernode']) == PerNode.NEVER:
110
111
  if Schema.GLOBAL_KEY in manifest_subsect['node'] and \
111
112
  Schema.GLOBAL_KEY in manifest_subsect['node'][Schema.GLOBAL_KEY]:
112
113
  value = manifest_subsect['node'][Schema.GLOBAL_KEY][Schema.GLOBAL_KEY]['value']
@@ -132,7 +133,7 @@ def make_manifest_helper(manifest_subsect, modified_manifest_subsect):
132
133
  return node_values
133
134
 
134
135
  if Schema._is_leaf(manifest_subsect):
135
- if manifest_subsect['pernode'] == 'never':
136
+ if PerNode(manifest_subsect['pernode']) == PerNode.NEVER:
136
137
  if Schema.GLOBAL_KEY in manifest_subsect['node']:
137
138
  value = manifest_subsect['node'][Schema.GLOBAL_KEY][Schema.GLOBAL_KEY]['value']
138
139
  else: