siliconcompiler 0.34.0__py3-none-any.whl → 0.34.2__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 (114) hide show
  1. siliconcompiler/__init__.py +14 -2
  2. siliconcompiler/_metadata.py +1 -1
  3. siliconcompiler/apps/_common.py +1 -1
  4. siliconcompiler/apps/sc.py +1 -1
  5. siliconcompiler/apps/sc_issue.py +1 -1
  6. siliconcompiler/apps/sc_remote.py +3 -3
  7. siliconcompiler/apps/sc_show.py +3 -3
  8. siliconcompiler/apps/utils/replay.py +4 -4
  9. siliconcompiler/checklist.py +203 -2
  10. siliconcompiler/constraints/__init__.py +17 -0
  11. siliconcompiler/constraints/asic_component.py +378 -0
  12. siliconcompiler/constraints/asic_floorplan.py +449 -0
  13. siliconcompiler/constraints/asic_pins.py +489 -0
  14. siliconcompiler/constraints/asic_timing.py +517 -0
  15. siliconcompiler/core.py +31 -249
  16. siliconcompiler/data/templates/email/general.j2 +3 -3
  17. siliconcompiler/data/templates/email/summary.j2 +1 -1
  18. siliconcompiler/data/templates/issue/README.txt +1 -1
  19. siliconcompiler/data/templates/report/sc_report.j2 +7 -7
  20. siliconcompiler/dependencyschema.py +10 -174
  21. siliconcompiler/design.py +325 -114
  22. siliconcompiler/flowgraph.py +63 -15
  23. siliconcompiler/library.py +133 -0
  24. siliconcompiler/metric.py +94 -72
  25. siliconcompiler/metrics/__init__.py +7 -0
  26. siliconcompiler/metrics/asic.py +245 -0
  27. siliconcompiler/metrics/fpga.py +220 -0
  28. siliconcompiler/optimizer/vizier.py +2 -2
  29. siliconcompiler/package/__init__.py +138 -35
  30. siliconcompiler/package/github.py +6 -10
  31. siliconcompiler/packageschema.py +256 -12
  32. siliconcompiler/pathschema.py +226 -0
  33. siliconcompiler/pdk.py +5 -5
  34. siliconcompiler/project.py +459 -0
  35. siliconcompiler/remote/client.py +18 -12
  36. siliconcompiler/remote/server.py +2 -2
  37. siliconcompiler/report/dashboard/cli/__init__.py +6 -6
  38. siliconcompiler/report/dashboard/cli/board.py +3 -3
  39. siliconcompiler/report/dashboard/web/components/__init__.py +5 -5
  40. siliconcompiler/report/dashboard/web/components/flowgraph.py +4 -4
  41. siliconcompiler/report/dashboard/web/components/graph.py +2 -2
  42. siliconcompiler/report/dashboard/web/state.py +1 -1
  43. siliconcompiler/report/dashboard/web/utils/__init__.py +5 -5
  44. siliconcompiler/report/html_report.py +1 -1
  45. siliconcompiler/report/report.py +4 -4
  46. siliconcompiler/report/summary_table.py +2 -2
  47. siliconcompiler/report/utils.py +5 -5
  48. siliconcompiler/scheduler/docker.py +4 -10
  49. siliconcompiler/scheduler/run_node.py +4 -8
  50. siliconcompiler/scheduler/scheduler.py +18 -24
  51. siliconcompiler/scheduler/schedulernode.py +161 -143
  52. siliconcompiler/scheduler/send_messages.py +3 -3
  53. siliconcompiler/scheduler/slurm.py +5 -3
  54. siliconcompiler/scheduler/taskscheduler.py +10 -8
  55. siliconcompiler/schema/__init__.py +0 -2
  56. siliconcompiler/schema/baseschema.py +148 -26
  57. siliconcompiler/schema/editableschema.py +14 -6
  58. siliconcompiler/schema/journal.py +23 -15
  59. siliconcompiler/schema/namedschema.py +30 -4
  60. siliconcompiler/schema/parameter.py +34 -19
  61. siliconcompiler/schema/parametertype.py +2 -0
  62. siliconcompiler/schema/parametervalue.py +198 -15
  63. siliconcompiler/schema/schema_cfg.py +18 -14
  64. siliconcompiler/schema_obj.py +5 -3
  65. siliconcompiler/tool.py +591 -179
  66. siliconcompiler/tools/__init__.py +2 -0
  67. siliconcompiler/tools/builtin/_common.py +5 -5
  68. siliconcompiler/tools/builtin/concatenate.py +5 -5
  69. siliconcompiler/tools/builtin/minimum.py +4 -4
  70. siliconcompiler/tools/builtin/mux.py +4 -4
  71. siliconcompiler/tools/builtin/nop.py +4 -4
  72. siliconcompiler/tools/builtin/verify.py +7 -7
  73. siliconcompiler/tools/execute/exec_input.py +1 -1
  74. siliconcompiler/tools/genfasm/genfasm.py +1 -6
  75. siliconcompiler/tools/openroad/_apr.py +5 -1
  76. siliconcompiler/tools/openroad/antenna_repair.py +1 -1
  77. siliconcompiler/tools/openroad/macro_placement.py +1 -1
  78. siliconcompiler/tools/openroad/power_grid.py +1 -1
  79. siliconcompiler/tools/openroad/scripts/common/procs.tcl +5 -0
  80. siliconcompiler/tools/opensta/timing.py +26 -3
  81. siliconcompiler/tools/slang/__init__.py +2 -2
  82. siliconcompiler/tools/surfer/__init__.py +0 -0
  83. siliconcompiler/tools/surfer/show.py +53 -0
  84. siliconcompiler/tools/surfer/surfer.py +30 -0
  85. siliconcompiler/tools/vpr/route.py +27 -14
  86. siliconcompiler/tools/vpr/vpr.py +23 -6
  87. siliconcompiler/tools/yosys/__init__.py +1 -1
  88. siliconcompiler/tools/yosys/scripts/procs.tcl +143 -0
  89. siliconcompiler/tools/yosys/{sc_synth_asic.tcl → scripts/sc_synth_asic.tcl} +4 -0
  90. siliconcompiler/tools/yosys/{sc_synth_fpga.tcl → scripts/sc_synth_fpga.tcl} +24 -77
  91. siliconcompiler/tools/yosys/syn_fpga.py +14 -0
  92. siliconcompiler/toolscripts/_tools.json +9 -13
  93. siliconcompiler/toolscripts/rhel9/install-vpr.sh +0 -2
  94. siliconcompiler/toolscripts/ubuntu22/install-surfer.sh +33 -0
  95. siliconcompiler/toolscripts/ubuntu24/install-surfer.sh +33 -0
  96. siliconcompiler/utils/__init__.py +2 -1
  97. siliconcompiler/utils/flowgraph.py +24 -23
  98. siliconcompiler/utils/issue.py +23 -29
  99. siliconcompiler/utils/logging.py +35 -6
  100. siliconcompiler/utils/showtools.py +6 -1
  101. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/METADATA +15 -25
  102. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/RECORD +109 -97
  103. siliconcompiler/schema/packageschema.py +0 -101
  104. siliconcompiler/tools/yosys/procs.tcl +0 -71
  105. siliconcompiler/toolscripts/rhel9/install-yosys-parmys.sh +0 -68
  106. siliconcompiler/toolscripts/ubuntu22/install-yosys-parmys.sh +0 -68
  107. siliconcompiler/toolscripts/ubuntu24/install-yosys-parmys.sh +0 -68
  108. /siliconcompiler/tools/yosys/{sc_lec.tcl → scripts/sc_lec.tcl} +0 -0
  109. /siliconcompiler/tools/yosys/{sc_screenshot.tcl → scripts/sc_screenshot.tcl} +0 -0
  110. /siliconcompiler/tools/yosys/{syn_strategies.tcl → scripts/syn_strategies.tcl} +0 -0
  111. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/WHEEL +0 -0
  112. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/entry_points.txt +0 -0
  113. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/licenses/LICENSE +0 -0
  114. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/top_level.txt +0 -0
siliconcompiler/core.py CHANGED
@@ -1,5 +1,6 @@
1
1
  # Copyright 2020 Silicon Compiler Authors. All Rights Reserved.
2
2
 
3
+ import copy
3
4
  import tarfile
4
5
  import os
5
6
  import pathlib
@@ -13,7 +14,6 @@ import shutil
13
14
  import importlib
14
15
  import textwrap
15
16
  import graphviz
16
- import codecs
17
17
  import csv
18
18
  import yaml
19
19
  from inspect import getfullargspec
@@ -23,10 +23,7 @@ from siliconcompiler.schema.parametertype import NodeType
23
23
  from siliconcompiler.schema.parametervalue import FileNodeValue
24
24
  from siliconcompiler.schema import utils as schema_utils
25
25
  from siliconcompiler import utils
26
- from siliconcompiler.utils.logging import SCColorLoggerFormatter, \
27
- SCLoggerFormatter, SCInRunLoggerFormatter, \
28
- SCDebugLoggerFormatter, SCDebugInRunLoggerFormatter, \
29
- SCBlankLoggerFormatter
26
+ from siliconcompiler.utils.logging import get_console_formatter, SCLoggerFormatter
30
27
  from siliconcompiler import _metadata
31
28
  from siliconcompiler import NodeStatus, SiliconCompilerError
32
29
  from siliconcompiler.report import _show_summary_table
@@ -40,6 +37,7 @@ from siliconcompiler.utils.flowgraph import _check_flowgraph_io, _get_flowgraph_
40
37
  from siliconcompiler.tools._common import get_tool_task
41
38
  from types import FunctionType, ModuleType
42
39
  from siliconcompiler.flowgraph import RuntimeFlowgraph
40
+ from siliconcompiler.package import Resolver
43
41
 
44
42
 
45
43
  class Chip:
@@ -62,6 +60,8 @@ class Chip:
62
60
  self.scversion = _metadata.version
63
61
  self.schemaversion = SCHEMA_VERSION
64
62
 
63
+ Resolver.reset_cache(self)
64
+
65
65
  # Local variables
66
66
  self.scroot = os.path.dirname(os.path.abspath(__file__))
67
67
  self._error = False
@@ -72,14 +72,13 @@ class Chip:
72
72
  "SiliconCompiler must be run from a directory that exists. "
73
73
  "If you are sure that your working directory is valid, try running `cd $(pwd)`.")
74
74
 
75
- # Initialize custom error handling for codecs. This has to be called
76
- # by each spawned (as opposed to forked) subprocess
77
- self._init_codecs()
78
-
79
- self._init_logger()
75
+ self.__init_logger()
80
76
 
81
77
  self.schema = Schema(logger=self.logger)
82
78
 
79
+ # Setup console formatting
80
+ self._logger_console.setFormatter(get_console_formatter(self, False, None, None))
81
+
83
82
  self.register_source('siliconcompiler',
84
83
  'python://siliconcompiler')
85
84
 
@@ -205,96 +204,21 @@ class Chip:
205
204
  else:
206
205
  return module
207
206
 
208
- def _add_file_logger(self, filename):
209
- # Add a file handler for logging
210
- file_handler = logging.FileHandler(filename)
211
- self.logger.addHandler(file_handler)
212
-
213
- self._init_logger_formats()
214
-
215
- return file_handler
216
-
217
207
  ###########################################################################
218
- def _init_logger(self, step=None, index=None, in_run=False):
219
-
208
+ def __init_logger(self):
220
209
  # Check if the logger exists and create
221
210
  if not hasattr(self, 'logger') or not self.logger:
222
211
  self.logger = logging.getLogger(f'sc_{id(self)}')
223
212
 
224
213
  self.logger.propagate = False
225
214
 
226
- loglevel = 'info'
227
- if hasattr(self, 'schema'):
228
- loglevel = self.schema.get('option', 'loglevel', step=step, index=index)
229
- else:
230
- in_run = False
231
-
232
- # Save in run flag
233
- self.logger._in_run = in_run
234
- self.logger._in_step = step
235
- self.logger._in_index = index
236
-
237
- self.logger.setLevel(schema_utils.translate_loglevel(loglevel))
238
-
239
- if not self.logger.hasHandlers():
240
- stream_handler = logging.StreamHandler(stream=sys.stdout)
241
- # Save console handler
242
- self.logger._console = stream_handler
243
- self.logger.addHandler(stream_handler)
244
-
245
- self.logger._support_color = SCColorLoggerFormatter.supports_color(stream_handler)
246
-
247
- self._init_logger_formats(loglevel=loglevel)
248
-
249
- def _init_logger_formats(self, loglevel=None):
250
- if not loglevel:
251
- loglevel = self.schema.get('option', 'loglevel',
252
- step=self.logger._in_step, index=self.logger._in_index)
253
-
254
- if loglevel == 'quiet':
255
- base_format = SCBlankLoggerFormatter()
256
- elif self.logger._in_run:
257
- if loglevel == 'debug':
258
- base_format = SCDebugInRunLoggerFormatter(
259
- self,
260
- self.get('option', 'jobname'),
261
- self.logger._in_step, self.logger._in_index)
262
- else:
263
- base_format = SCInRunLoggerFormatter(
264
- self,
265
- self.get('option', 'jobname'),
266
- self.logger._in_step, self.logger._in_index)
267
- else:
268
- if loglevel == 'debug':
269
- base_format = SCDebugLoggerFormatter()
270
- else:
271
- base_format = SCLoggerFormatter()
215
+ self.logger.setLevel(logging.INFO)
272
216
 
273
- for handler in self.logger.handlers.copy():
274
- if handler == self.logger._console and self.logger._support_color:
275
- formatter = SCColorLoggerFormatter(base_format)
276
- else:
277
- formatter = base_format
278
- handler.setFormatter(formatter)
279
-
280
- ###########################################################################
281
- def _init_codecs(self):
282
- # Custom error handlers used to provide warnings when invalid characters
283
- # are encountered in a file for a given encoding. The names
284
- # 'replace_with_warning' and 'ignore_with_warning' are supplied to
285
- # open() via the 'errors' kwarg.
286
-
287
- # Warning message/behavior for invalid characters while running tool
288
- def display_error_handler(e):
289
- self.logger.warning('Invalid character in tool output, displaying as �')
290
- return codecs.replace_errors(e)
291
- codecs.register_error('replace_with_warning', display_error_handler)
292
-
293
- # Warning message/behavior for invalid characters while processing log
294
- def log_error_handler(e):
295
- self.logger.warning('Ignoring invalid character found while reading log')
296
- return codecs.ignore_errors(e)
297
- codecs.register_error('ignore_with_warning', log_error_handler)
217
+ stream_handler = logging.StreamHandler(stream=sys.stdout)
218
+ stream_handler.setFormatter(SCLoggerFormatter())
219
+ # Save console handler
220
+ self._logger_console = stream_handler
221
+ self.logger.addHandler(stream_handler)
298
222
 
299
223
  ###########################################################################
300
224
  def create_cmdline(self,
@@ -1557,12 +1481,9 @@ class Chip:
1557
1481
  if keypath[-2:] == ('option', 'builddir'):
1558
1482
  ignore_keys.append(keypath)
1559
1483
 
1560
- package_map = self.get("package", field="schema").get_resolvers()
1561
-
1562
1484
  return self.schema.check_filepaths(
1563
1485
  ignore_keys=ignore_keys,
1564
1486
  logger=self.logger,
1565
- packages=package_map,
1566
1487
  collection_dir=self._getcollectdir(),
1567
1488
  cwd=self.cwd)
1568
1489
 
@@ -1631,7 +1552,7 @@ class Chip:
1631
1552
  NodeStatus.SUCCESS:
1632
1553
  # this task has already completed successfully, OK
1633
1554
  continue
1634
- self.logger.error(f'{step}{index} relies on {in_step}{in_index}, '
1555
+ self.logger.error(f'{step}/{index} relies on {in_step}/{in_index}, '
1635
1556
  'but this task has not been run and is not in the '
1636
1557
  'current nodes to execute.')
1637
1558
  error = True
@@ -1676,12 +1597,12 @@ class Chip:
1676
1597
  if not self._get_tool_module(step, index, flow=flow, error=False):
1677
1598
  error = True
1678
1599
  self.logger.error(f"Tool module {tool_name} could not be found or "
1679
- f"loaded for {step}{index}.")
1600
+ f"loaded for {step}/{index}.")
1680
1601
  if not self._get_task_module(step, index, flow=flow, error=False):
1681
1602
  error = True
1682
1603
  task_module = self.get('flowgraph', flow, step, index, 'taskmodule')
1683
1604
  self.logger.error(f"Task module {task_module} for {tool_name}/{task_name} "
1684
- f"could not be found or loaded for {step}{index}.")
1605
+ f"could not be found or loaded for {step}/{index}.")
1685
1606
 
1686
1607
  # 5. Check per tool parameter requirements (when tool exists)
1687
1608
  for (step, index) in nodes:
@@ -1911,156 +1832,13 @@ class Chip:
1911
1832
  >>> status = chip.check_checklist('iso9000', 'd000')
1912
1833
  Returns status.
1913
1834
  '''
1914
- error = False
1915
-
1916
- self.logger.info(f'Checking checklist {standard}')
1917
-
1918
1835
  if standard not in self.getkeys('checklist'):
1919
1836
  self.logger.error(f'{standard} has not been loaded.')
1920
1837
  return False
1921
1838
 
1922
- if items is None:
1923
- items = self.getkeys('checklist', standard)
1924
-
1925
- # these tasks are recorded by SC so there are no reports
1926
- metrics_without_reports = (
1927
- 'tasktime',
1928
- 'totaltime',
1929
- 'exetime',
1930
- 'memory')
1931
-
1932
- for item in items:
1933
- if item not in self.getkeys('checklist', standard):
1934
- self.logger.error(f'{item} is not a check in {standard}.')
1935
- error = True
1936
- continue
1937
-
1938
- allow_missing_reports = True
1939
-
1940
- has_check = False
1941
-
1942
- all_criteria = self.get('checklist', standard, item, 'criteria')
1943
- for criteria in all_criteria:
1944
- m = re.match(r'^(\w+)\s*([\>\=\<]+)\s*([+\-]?\d+(\.\d+)?(e[+\-]?\d+)?)$',
1945
- criteria.strip())
1946
- if not m:
1947
- self.error(f"Illegal checklist criteria: {criteria}")
1948
- return False
1949
- elif m.group(1) not in self.getkeys('metric'):
1950
- self.error(f"Criteria must use legal metrics only: {criteria}")
1951
- return False
1952
-
1953
- metric = m.group(1)
1954
- op = m.group(2)
1955
- if self.get('metric', metric, field='type') == 'int':
1956
- goal = int(m.group(3))
1957
- number_format = 'd'
1958
- else:
1959
- goal = float(m.group(3))
1960
-
1961
- if goal == 0.0 or (abs(goal) > 1e-3 and abs(goal) < 1e5):
1962
- number_format = '.3f'
1963
- else:
1964
- number_format = '.3e'
1965
-
1966
- if metric not in metrics_without_reports:
1967
- allow_missing_reports = False
1968
-
1969
- tasks = self.get('checklist', standard, item, 'task')
1970
- for job, step, index in tasks:
1971
- if job not in self.getkeys('history'):
1972
- self.error(f'{job} not found in history')
1973
-
1974
- flow = self.get('option', 'flow', job=job)
1975
-
1976
- if step not in self.getkeys('flowgraph', flow, job=job):
1977
- self.error(f'{step} not found in flowgraph')
1978
-
1979
- if index not in self.getkeys('flowgraph', flow, step, job=job):
1980
- self.error(f'{step}{index} not found in flowgraph')
1981
-
1982
- if self.get('record', 'status', step=step, index=index, job=job) == \
1983
- NodeStatus.SKIPPED:
1984
- if verbose:
1985
- self.logger.warning(f'{step}{index} was skipped')
1986
- continue
1987
-
1988
- has_check = True
1989
-
1990
- # Automated checks
1991
- flow = self.get('option', 'flow', job=job)
1992
- tool = self.get('flowgraph', flow, step, index, 'tool', job=job)
1993
- task = self.get('flowgraph', flow, step, index, 'task', job=job)
1994
-
1995
- value = self.get('metric', metric, job=job, step=step, index=index)
1996
- criteria_ok = utils.safecompare(self, value, op, goal)
1997
- if metric in self.getkeys('checklist', standard, item, 'waiver'):
1998
- waivers = self.get('checklist', standard, item, 'waiver', metric)
1999
- else:
2000
- waivers = []
2001
-
2002
- criteria_str = f'{metric}{op}{goal:{number_format}}'
2003
- compare_str = f'{value:{number_format}}{op}{goal:{number_format}}'
2004
- step_desc = f'job {job} with step {step}{index} and task {tool}/{task}'
2005
- if not criteria_ok and waivers:
2006
- self.logger.warning(f'{item} criteria {criteria_str} ({compare_str}) unmet '
2007
- f'by {step_desc}, but found waivers.')
2008
- elif not criteria_ok:
2009
- self.logger.error(f'{item} criteria {criteria_str} ({compare_str}) unmet '
2010
- f'by {step_desc}.')
2011
- error = True
2012
- elif verbose and criteria_ok:
2013
- self.logger.info(f'{item} criteria {criteria_str} met by {step_desc}.')
2014
-
2015
- has_reports = \
2016
- self.valid('tool', tool, 'task', task, 'report', metric, job=job) and \
2017
- self.get('tool', tool, 'task', task, 'report', metric, job=job,
2018
- step=step, index=index)
2019
-
2020
- if allow_missing_reports and not has_reports:
2021
- # No reports available and it is allowed
2022
- continue
2023
-
2024
- reports = []
2025
- try:
2026
- if has_reports:
2027
- reports = self.find_files('tool', tool, 'task', task, 'report', metric,
2028
- job=job,
2029
- step=step, index=index,
2030
- missing_ok=not require_reports)
2031
- except SiliconCompilerError:
2032
- reports = []
2033
- continue
2034
-
2035
- if require_reports and not reports:
2036
- self.logger.error(f'No EDA reports generated for metric {metric} in '
2037
- f'{step_desc}')
2038
- error = True
2039
-
2040
- for report in reports:
2041
- if not report:
2042
- continue
2043
-
2044
- report = os.path.relpath(report, self.cwd)
2045
- if report not in self.get('checklist', standard, item, 'report'):
2046
- self.add('checklist', standard, item, 'report', report)
2047
-
2048
- if has_check:
2049
- if require_reports and \
2050
- not allow_missing_reports and \
2051
- not self.get('checklist', standard, item, 'report'):
2052
- # TODO: validate that report exists?
2053
- self.logger.error(f'No report documenting item {item}')
2054
- error = True
2055
-
2056
- if check_ok and not self.get('checklist', standard, item, 'ok'):
2057
- self.logger.error(f"Item {item} 'ok' field not checked")
2058
- error = True
2059
-
2060
- if not error:
2061
- self.logger.info('Check succeeded!')
2062
-
2063
- return not error
1839
+ return self.get("checklist", standard, field="schema").check(
1840
+ items=items, check_ok=check_ok, require_reports=require_reports
1841
+ )
2064
1842
 
2065
1843
  ###########################################################################
2066
1844
  def __import_library(self, libname, library, job=None, clobber=True, keep_input=True):
@@ -2673,7 +2451,7 @@ class Chip:
2673
2451
  ###########################################################################
2674
2452
  def _archive_node(self, tar, step, index, include=None, verbose=True):
2675
2453
  if verbose:
2676
- self.logger.info(f'Archiving {step}{index}...')
2454
+ self.logger.info(f'Archiving {step}/{index}...')
2677
2455
 
2678
2456
  basedir = self.getworkdir(step=step, index=index)
2679
2457
 
@@ -2682,7 +2460,7 @@ class Chip:
2682
2460
 
2683
2461
  if not os.path.isdir(basedir):
2684
2462
  if self.get('record', 'status', step=step, index=index) != NodeStatus.SKIPPED:
2685
- self.logger.error(f'Unable to archive {step}{index} due to missing node directory')
2463
+ self.logger.error(f'Unable to archive {step}/{index} due to missing node directory')
2686
2464
  return
2687
2465
 
2688
2466
  if include:
@@ -2756,7 +2534,7 @@ class Chip:
2756
2534
 
2757
2535
  if not archive_name:
2758
2536
  if step and index:
2759
- archive_name = f"{design}_{jobname}_{step}{index}.tgz"
2537
+ archive_name = f"{design}_{jobname}_{step}_{index}.tgz"
2760
2538
  elif step:
2761
2539
  archive_name = f"{design}_{jobname}_{step}.tgz"
2762
2540
  else:
@@ -3322,7 +3100,7 @@ class Chip:
3322
3100
  self.unset('option', 'prune')
3323
3101
  self.unset('option', 'from')
3324
3102
  # build new job name
3325
- self.set('option', 'jobname', f'_{taskname}_{sc_job}_{sc_step}{sc_index}', clobber=True)
3103
+ self.set('option', 'jobname', f'_{taskname}_{sc_job}_{sc_step}_{sc_index}', clobber=True)
3326
3104
 
3327
3105
  # Setup in step/index variables
3328
3106
  for step, index in self.get("flowgraph", "showflow", field="schema").get_nodes():
@@ -3451,6 +3229,7 @@ class Chip:
3451
3229
  # We have to remove the chip's logger before serializing the object
3452
3230
  # since the logger object is not serializable.
3453
3231
  del attributes['logger']
3232
+ del attributes['_logger_console']
3454
3233
  return attributes
3455
3234
 
3456
3235
  #######################################
@@ -3458,4 +3237,7 @@ class Chip:
3458
3237
  self.__dict__ = state
3459
3238
 
3460
3239
  # Reinitialize logger on restore
3461
- self._init_logger()
3240
+ self.__init_logger()
3241
+
3242
+ def copy(self):
3243
+ return copy.deepcopy(self)
@@ -9,11 +9,11 @@
9
9
  </head>
10
10
 
11
11
  <body>
12
- <h2>Node Summary: "{{ step }}{{ index }}"</h2>
12
+ <h2>Node Summary: "{{ step }}/{{ index }}"</h2>
13
13
  <span>
14
14
  <p><b>Design</b>: {{ design }}</p>
15
15
  <p><b>Job</b>: {{ job }}</p>
16
- <p><b>Node</b>: {{ step }}{{ index }}</p>
16
+ <p><b>Node</b>: {{ step }}/{{ index }}</p>
17
17
  <p><b>Status</b>: {{ status }}</p>
18
18
  </span>
19
19
  <span>
@@ -36,7 +36,7 @@
36
36
  <th>Metrics</th>
37
37
  <th>units</th>
38
38
  {% for step, index in nodes %}
39
- <th align="center">{{ step }}{{ index }}</th>
39
+ <th align="center">{{ step }}/{{ index }}</th>
40
40
  {% endfor %}
41
41
  </tr>
42
42
  {% for metric in metric_keys %}
@@ -14,7 +14,7 @@
14
14
  <th>-</th>
15
15
  <th>units</th>
16
16
  {% for step, index in nodes %}
17
- <th align="center">{{ step }}{{ index }}</th>
17
+ <th align="center">{{ step }}/{{ index }}</th>
18
18
  {% endfor %}
19
19
  </tr>
20
20
  {% for metric in metric_keys %}
@@ -13,7 +13,7 @@ Schema: {{ version['schema'] }}
13
13
  Testcase built: {{ date }}
14
14
  Tool: {{ run['tool'] }} {% if run['toolversion'] %}{{ run['toolversion'] }}{% endif %}
15
15
  Task: {{ run['task'] }}
16
- Node: {{ run['step'] }}{{ run['index'] }}
16
+ Node: {{ run['step'] }}/{{ run['index'] }}
17
17
 
18
18
  ** Python **
19
19
  Version: {{ python['version'] }}
@@ -271,12 +271,12 @@
271
271
  {% for metric in metric_keys %}
272
272
  {% for step, index in nodes %}
273
273
  {% if reports[step, index][metric] %}
274
- var sim_log_btn = document.getElementById("{{ step }}{{ index }}_{{ metric }}_metlink");
274
+ var sim_log_btn = document.getElementById("{{ step }}_{{ index }}_{{ metric }}_metlink");
275
275
  sim_log_btn.addEventListener('click', () => {
276
276
  log_link = "{{ step }}/{{ index }}/{{ reports[step, index][metric][0] }}";
277
277
  window.open(log_link, "_blank");
278
278
  });
279
- var sim_log_btn = document.getElementById("{{ step }}{{ index }}_{{ metric }}_ddmetlink");
279
+ var sim_log_btn = document.getElementById("{{ step }}_{{ index }}_{{ metric }}_ddmetlink");
280
280
  sim_log_btn.addEventListener('click', () => {
281
281
  log_link = "{{ step }}/{{ index }}/{{ reports[step, index][metric][0] }}";
282
282
  window.open(log_link, "_blank");
@@ -333,7 +333,7 @@
333
333
  <th>-</th>
334
334
  <th>units</th>
335
335
  {% for step, index in nodes %}
336
- <th>{{ step }}{{ index }}</th>
336
+ <th>{{ step }}/{{ index }}</th>
337
337
  {% endfor %}
338
338
  </tr>
339
339
  {% for metric in metric_keys %}
@@ -372,12 +372,12 @@
372
372
  <h2>Metrics for {{ design }} Tasks</h2>
373
373
  {% for step, index in nodes %}
374
374
  <div>
375
- <a class="btn btn-success" data-bs-toggle="collapse" href="#{{ step }}{{ index }}_dropdown_div", role="button", aria-controls="{{ step }}{{ index }}_dropdown_div">
376
- Toggle {{ step }}{{ index }} Metrics
375
+ <a class="btn btn-success" data-bs-toggle="collapse" href="#{{ step }}_{{ index }}_dropdown_div", role="button", aria-controls="{{ step }}_{{ index }}_dropdown_div">
376
+ Toggle {{ step }}/{{ index }} Metrics
377
377
  </a>
378
378
  </div>
379
- <div id="{{ step }}{{ index }}_dropdown_div" class="collapse">
380
- <table id="{{ step }}{{ index }}_metrics_table" class="table table-dark table-striped table-bordered">
379
+ <div id="{{ step }}_{{ index }}_dropdown_div" class="collapse">
380
+ <table id="{{ step }}_{{ index }}_metrics_table" class="table table-dark table-striped table-bordered">
381
381
  <tr>
382
382
  {% for metric in metric_keys %}
383
383
  <th>{{ metric }}</th>