siliconcompiler 0.33.2__py3-none-any.whl → 0.34.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 (104) hide show
  1. siliconcompiler/__init__.py +2 -0
  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 +6 -4
  6. siliconcompiler/apps/sc_remote.py +3 -20
  7. siliconcompiler/apps/sc_show.py +2 -2
  8. siliconcompiler/apps/utils/replay.py +4 -4
  9. siliconcompiler/checklist.py +202 -1
  10. siliconcompiler/core.py +62 -293
  11. siliconcompiler/data/templates/email/general.j2 +3 -3
  12. siliconcompiler/data/templates/email/summary.j2 +1 -1
  13. siliconcompiler/data/templates/issue/README.txt +1 -1
  14. siliconcompiler/data/templates/report/sc_report.j2 +7 -7
  15. siliconcompiler/dependencyschema.py +392 -0
  16. siliconcompiler/design.py +758 -0
  17. siliconcompiler/flowgraph.py +79 -13
  18. siliconcompiler/optimizer/vizier.py +2 -2
  19. siliconcompiler/package/__init__.py +383 -223
  20. siliconcompiler/package/git.py +75 -77
  21. siliconcompiler/package/github.py +70 -97
  22. siliconcompiler/package/https.py +77 -93
  23. siliconcompiler/packageschema.py +260 -0
  24. siliconcompiler/pdk.py +5 -5
  25. siliconcompiler/remote/client.py +33 -15
  26. siliconcompiler/remote/server.py +2 -2
  27. siliconcompiler/report/dashboard/cli/__init__.py +6 -6
  28. siliconcompiler/report/dashboard/cli/board.py +4 -4
  29. siliconcompiler/report/dashboard/web/components/__init__.py +5 -5
  30. siliconcompiler/report/dashboard/web/components/flowgraph.py +4 -4
  31. siliconcompiler/report/dashboard/web/components/graph.py +2 -2
  32. siliconcompiler/report/dashboard/web/state.py +1 -1
  33. siliconcompiler/report/dashboard/web/utils/__init__.py +5 -5
  34. siliconcompiler/report/html_report.py +1 -1
  35. siliconcompiler/report/report.py +4 -4
  36. siliconcompiler/report/summary_table.py +2 -2
  37. siliconcompiler/report/utils.py +5 -5
  38. siliconcompiler/scheduler/__init__.py +3 -1382
  39. siliconcompiler/scheduler/docker.py +263 -0
  40. siliconcompiler/scheduler/run_node.py +10 -21
  41. siliconcompiler/scheduler/scheduler.py +311 -0
  42. siliconcompiler/scheduler/schedulernode.py +944 -0
  43. siliconcompiler/scheduler/send_messages.py +3 -3
  44. siliconcompiler/scheduler/slurm.py +149 -163
  45. siliconcompiler/scheduler/taskscheduler.py +45 -57
  46. siliconcompiler/schema/__init__.py +3 -3
  47. siliconcompiler/schema/baseschema.py +234 -11
  48. siliconcompiler/schema/editableschema.py +4 -0
  49. siliconcompiler/schema/journal.py +210 -0
  50. siliconcompiler/schema/namedschema.py +55 -2
  51. siliconcompiler/schema/parameter.py +14 -1
  52. siliconcompiler/schema/parametervalue.py +1 -34
  53. siliconcompiler/schema/schema_cfg.py +210 -349
  54. siliconcompiler/tool.py +412 -148
  55. siliconcompiler/tools/__init__.py +2 -0
  56. siliconcompiler/tools/builtin/_common.py +5 -5
  57. siliconcompiler/tools/builtin/concatenate.py +7 -7
  58. siliconcompiler/tools/builtin/minimum.py +4 -4
  59. siliconcompiler/tools/builtin/mux.py +4 -4
  60. siliconcompiler/tools/builtin/nop.py +4 -4
  61. siliconcompiler/tools/builtin/verify.py +8 -9
  62. siliconcompiler/tools/execute/exec_input.py +1 -1
  63. siliconcompiler/tools/genfasm/genfasm.py +1 -6
  64. siliconcompiler/tools/openroad/_apr.py +5 -1
  65. siliconcompiler/tools/openroad/antenna_repair.py +1 -1
  66. siliconcompiler/tools/openroad/macro_placement.py +1 -1
  67. siliconcompiler/tools/openroad/power_grid.py +1 -1
  68. siliconcompiler/tools/openroad/scripts/common/procs.tcl +32 -25
  69. siliconcompiler/tools/opensta/timing.py +26 -3
  70. siliconcompiler/tools/slang/__init__.py +2 -2
  71. siliconcompiler/tools/surfer/__init__.py +0 -0
  72. siliconcompiler/tools/surfer/show.py +53 -0
  73. siliconcompiler/tools/surfer/surfer.py +30 -0
  74. siliconcompiler/tools/vpr/route.py +82 -0
  75. siliconcompiler/tools/vpr/vpr.py +23 -6
  76. siliconcompiler/tools/yosys/__init__.py +1 -1
  77. siliconcompiler/tools/yosys/scripts/procs.tcl +143 -0
  78. siliconcompiler/tools/yosys/{sc_synth_asic.tcl → scripts/sc_synth_asic.tcl} +4 -0
  79. siliconcompiler/tools/yosys/{sc_synth_fpga.tcl → scripts/sc_synth_fpga.tcl} +24 -77
  80. siliconcompiler/tools/yosys/syn_fpga.py +14 -0
  81. siliconcompiler/toolscripts/_tools.json +9 -13
  82. siliconcompiler/toolscripts/rhel9/install-vpr.sh +0 -2
  83. siliconcompiler/toolscripts/ubuntu22/install-surfer.sh +33 -0
  84. siliconcompiler/toolscripts/ubuntu24/install-surfer.sh +33 -0
  85. siliconcompiler/utils/__init__.py +4 -24
  86. siliconcompiler/utils/flowgraph.py +29 -28
  87. siliconcompiler/utils/issue.py +23 -29
  88. siliconcompiler/utils/logging.py +37 -7
  89. siliconcompiler/utils/showtools.py +6 -1
  90. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.1.dist-info}/METADATA +16 -25
  91. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.1.dist-info}/RECORD +98 -91
  92. siliconcompiler/scheduler/docker_runner.py +0 -254
  93. siliconcompiler/schema/journalingschema.py +0 -242
  94. siliconcompiler/tools/yosys/procs.tcl +0 -71
  95. siliconcompiler/toolscripts/rhel9/install-yosys-parmys.sh +0 -68
  96. siliconcompiler/toolscripts/ubuntu22/install-yosys-parmys.sh +0 -68
  97. siliconcompiler/toolscripts/ubuntu24/install-yosys-parmys.sh +0 -68
  98. /siliconcompiler/tools/yosys/{sc_lec.tcl → scripts/sc_lec.tcl} +0 -0
  99. /siliconcompiler/tools/yosys/{sc_screenshot.tcl → scripts/sc_screenshot.tcl} +0 -0
  100. /siliconcompiler/tools/yosys/{syn_strategies.tcl → scripts/syn_strategies.tcl} +0 -0
  101. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.1.dist-info}/WHEEL +0 -0
  102. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.1.dist-info}/entry_points.txt +0 -0
  103. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.1.dist-info}/licenses/LICENSE +0 -0
  104. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.1.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,7 @@ from siliconcompiler._common import NodeStatus, SiliconCompilerError
3
3
  from siliconcompiler.utils import sc_open
4
4
  from siliconcompiler.schema_obj import SchemaTmp as Schema
5
5
 
6
+ from siliconcompiler.design import DesignSchema
6
7
  from siliconcompiler.record import RecordSchema
7
8
  from siliconcompiler.metric import MetricSchema
8
9
  from siliconcompiler.pdk import PDKSchema
@@ -30,6 +31,7 @@ __all__ = [
30
31
  "Checklist",
31
32
  "Schema",
32
33
  "sc_open",
34
+ "DesignSchema",
33
35
  "RecordSchema",
34
36
  "MetricSchema",
35
37
  "PDKSchema",
@@ -1,5 +1,5 @@
1
1
  # Version number following semver standard.
2
- version = '0.33.2'
2
+ version = '0.34.1'
3
3
 
4
4
  # Default server address for remote runs, if unspecified.
5
5
  default_server = 'https://server.siliconcompiler.com'
@@ -114,7 +114,7 @@ def pick_manifest(chip, src_file=None):
114
114
  if (step, index) in all_manifests[chip.design][jobname]:
115
115
  return all_manifests[chip.design][jobname][(step, index)]
116
116
  else:
117
- chip.logger.error(f'{step}{index} is not a valid node.')
117
+ chip.logger.error(f'{step}/{index} is not a valid node.')
118
118
  return None
119
119
 
120
120
  if (None, None) in all_manifests[chip.design][jobname]:
@@ -19,7 +19,7 @@ def _infer_designname(chip):
19
19
  sourcesets.insert(0, sourceset)
20
20
  for sourceset in sourcesets:
21
21
  for filetype in chip.getkeys('input', sourceset):
22
- all_vals = chip.schema.get('input', sourceset, filetype, field=None).getvalues()
22
+ all_vals = chip.get('input', sourceset, filetype, field=None).getvalues()
23
23
  if all_vals:
24
24
  # just look at first value
25
25
  sources, _, _ = all_vals[0]
@@ -4,7 +4,7 @@ import os
4
4
  import siliconcompiler
5
5
  import tarfile
6
6
  import json
7
- from siliconcompiler.scheduler import _runtask, _executenode
7
+ from siliconcompiler.scheduler.schedulernode import SchedulerNode
8
8
  from siliconcompiler.utils.issue import generate_testcase
9
9
  from siliconcompiler.tools._common import get_tool_task
10
10
 
@@ -142,7 +142,7 @@ To run a testcase, use:
142
142
  chip.set('arg', 'step', step)
143
143
  chip.set('arg', 'index', index)
144
144
  tool, task = get_tool_task(chip, step, index)
145
- chip.logger.info(f'Preparing run for {step}{index} - {tool}/{task}')
145
+ chip.logger.info(f'Preparing run for {step}/{index} - {tool}/{task}')
146
146
 
147
147
  # Modify run environment to point to extracted files
148
148
  builddir_key = ['option', 'builddir']
@@ -153,8 +153,10 @@ To run a testcase, use:
153
153
  # Run task
154
154
  # Rerun setup task, assumed to be running in its own thread so
155
155
  # multiprocess is not needed
156
- flow = chip.get('option', 'flow')
157
- _runtask(chip, flow, step, index, _executenode, replay=True)
156
+ SchedulerNode(chip,
157
+ step,
158
+ index,
159
+ replay=True).run()
158
160
 
159
161
  return 0
160
162
 
@@ -1,12 +1,9 @@
1
1
  # Copyright 2023 Silicon Compiler Authors. All Rights Reserved.
2
- import os
3
2
  import sys
4
3
 
5
4
  from siliconcompiler import Chip
6
5
  from siliconcompiler import SiliconCompilerError
7
6
  from siliconcompiler.remote.client import Client, ConfigureClient
8
- from siliconcompiler.scheduler import _finalize_run
9
- from siliconcompiler.flowgraph import RuntimeFlowgraph
10
7
 
11
8
 
12
9
  def main():
@@ -157,10 +154,10 @@ To delete a job, use:
157
154
  elif args['reconnect']:
158
155
  # Start from successors of entry nodes, so entry nodes are not fetched from remote.
159
156
  flow = chip.get('option', 'flow')
160
- entry_nodes = chip.schema.get("flowgraph", flow, field="schema").get_entry_nodes()
157
+ entry_nodes = chip.get("flowgraph", flow, field="schema").get_entry_nodes()
161
158
  for entry_node in entry_nodes:
162
- outputs = chip.schema.get("flowgraph", flow,
163
- field='schema').get_node_outputs(*entry_node)
159
+ outputs = chip.get("flowgraph", flow,
160
+ field='schema').get_node_outputs(*entry_node)
164
161
  chip.set('option', 'from', list(map(lambda node: node[0], outputs)))
165
162
  # Enter the remote run loop.
166
163
  try:
@@ -169,20 +166,6 @@ To delete a job, use:
169
166
  chip.logger.error(f'{e}')
170
167
  return 1
171
168
 
172
- # Wrap up run
173
- runtime = RuntimeFlowgraph(
174
- chip.schema.get("flowgraph", flow, field='schema'),
175
- from_steps=chip.get('option', 'from'),
176
- to_steps=chip.get('option', 'to'),
177
- prune_nodes=chip.get('option', 'prune'))
178
- for step, index in runtime.get_nodes():
179
- manifest = os.path.join(chip.getworkdir(step=step, index=index),
180
- 'outputs',
181
- f'{chip.design}.pkg.json')
182
- if os.path.exists(manifest):
183
- chip.schema.read_journal(manifest)
184
- _finalize_run(chip)
185
-
186
169
  # Summarize the run.
187
170
  chip.summary()
188
171
 
@@ -84,7 +84,7 @@ def main():
84
84
  input_mode = []
85
85
  for fileset in chip.getkeys('input'):
86
86
  for mode in chip.getkeys('input', fileset):
87
- if chip.schema.get('input', fileset, mode, field=None).getvalues():
87
+ if chip.get('input', fileset, mode, field=None).getvalues():
88
88
  input_mode = [('input', fileset, mode)]
89
89
 
90
90
  filename = None
@@ -97,7 +97,7 @@ def main():
97
97
  def get_file_from_keys():
98
98
  for ext in check_ext:
99
99
  for key in input_mode:
100
- for files, _, _ in chip.schema.get(*key, field=None).getvalues():
100
+ for files, _, _ in chip.get(*key, field=None).getvalues():
101
101
  for file in files:
102
102
  if get_file_ext(file) == ext:
103
103
  return file
@@ -87,8 +87,8 @@ def main():
87
87
 
88
88
  pythonversion = set()
89
89
  nodes = set()
90
- for version, step, index in chip.schema.get('history', jobname, 'record', 'pythonversion',
91
- field=None).getvalues():
90
+ for version, step, index in chip.get('history', jobname, 'record', 'pythonversion',
91
+ field=None).getvalues():
92
92
  pythonversion.add(version)
93
93
  nodes.add((step, index))
94
94
 
@@ -128,7 +128,7 @@ def main():
128
128
  path = os.path.abspath(args['file'])
129
129
  os.makedirs(os.path.dirname(path), exist_ok=True)
130
130
 
131
- record_schema = chip.schema.get('history', jobname, 'record', field="schema")
131
+ record_schema = chip.get('history', jobname, 'record', field="schema")
132
132
  starttime = datetime.fromtimestamp(
133
133
  record_schema.get_earliest_time(RecordTime.START)).strftime(
134
134
  '%Y-%m-%d %H:%M:%S')
@@ -164,7 +164,7 @@ def main():
164
164
  fd.flush()
165
165
  script = convert_base64(compress(fd.getvalue()))
166
166
 
167
- manifest = convert_base64(compress(json.dumps(chip.schema.getdict(), indent=2)))
167
+ manifest = convert_base64(compress(json.dumps(chip.getdict(), indent=2)))
168
168
 
169
169
  tool_info = []
170
170
  for tool, version in tools.items():
@@ -1,14 +1,215 @@
1
+ import re
2
+
3
+ import os.path
4
+
1
5
  from siliconcompiler.schema import NamedSchema
2
6
  from siliconcompiler.schema import EditableSchema, Parameter, Scope
3
7
  from siliconcompiler.schema.utils import trim
4
8
 
9
+ from siliconcompiler import NodeStatus, utils
10
+
5
11
 
6
12
  class ChecklistSchema(NamedSchema):
7
13
  def __init__(self, name=None):
8
- super().__init__(name=name)
14
+ super().__init__()
15
+ self.set_name(name)
9
16
 
10
17
  schema_checklist(self)
11
18
 
19
+ def check(self, items=None, check_ok=False, require_reports=True):
20
+ '''
21
+ Check items in a checklist.
22
+
23
+ Checks the status of items in a checklist for the standard provided. If
24
+ a specific list of items is unspecified, all items are checked.
25
+
26
+ All items have an associated 'task' parameter, which indicates which
27
+ tasks can be used to automatically validate the item. For an item to be
28
+ checked, all tasks must satisfy the item's criteria, unless waivers are
29
+ provided. In addition, that task must have generated EDA report files
30
+ for each metric in the criteria.
31
+
32
+ For items without an associated task, the only requirement is that at
33
+ least one report has been added to that item.
34
+
35
+ When 'check_ok' is True, every item must also have its 'ok' parameter
36
+ set to True, indicating that a human has reviewed the item.
37
+
38
+ Args:
39
+ items (list of str): Items to check from standard.
40
+ check_ok (bool): Whether to check item 'ok' parameter.
41
+ verbose (bool): Whether to print passing criteria.
42
+ require_reports (bool): Whether to assert the presence of reports.
43
+
44
+ Returns:
45
+ Status of item check.
46
+
47
+ Examples:
48
+ >>> status = chip.check_checklist('d000')
49
+ Returns status.
50
+ '''
51
+ error = False
52
+
53
+ schema_root = self._parent(root=True)
54
+ logger = getattr(schema_root, "logger", None)
55
+ cwd = getattr(schema_root, "cwd", os.getcwd())
56
+
57
+ assert hasattr(schema_root, "history"), f"{schema_root}"
58
+
59
+ if logger:
60
+ logger.info(f'Checking checklist {self.name()}')
61
+
62
+ if items is None:
63
+ items = self.getkeys()
64
+
65
+ # these tasks are recorded by SC so there are no reports
66
+ metrics_without_reports = (
67
+ 'tasktime',
68
+ 'totaltime',
69
+ 'exetime',
70
+ 'memory')
71
+
72
+ for item in items:
73
+ if item not in self.getkeys():
74
+ if logger:
75
+ logger.error(f'{item} is not a check in {self.name()}.')
76
+ error = True
77
+ continue
78
+
79
+ allow_missing_reports = True
80
+
81
+ has_check = False
82
+
83
+ all_criteria = self.get(item, 'criteria')
84
+ for criteria in all_criteria:
85
+ m = re.match(r'^(\w+)\s*([\>\=\<]+)\s*([+\-]?\d+(\.\d+)?(e[+\-]?\d+)?)$',
86
+ criteria.strip())
87
+ if not m:
88
+ raise ValueError(f"Illegal checklist criteria: {criteria}")
89
+
90
+ metric = m.group(1)
91
+ op = m.group(2)
92
+
93
+ if metric not in metrics_without_reports:
94
+ allow_missing_reports = False
95
+
96
+ tasks = self.get(item, 'task')
97
+ for job, step, index in tasks:
98
+ job_data = schema_root.history(job)
99
+
100
+ flow = job_data.get("flowgraph", job_data.get('option', 'flow'), field="schema")
101
+
102
+ if (step, index) not in flow.get_nodes():
103
+ error = True
104
+ if logger:
105
+ logger.error(f'{step}/{index} not found in flowgraph for {job}')
106
+ continue
107
+
108
+ if job_data.get('record', 'status', step=step, index=index) == \
109
+ NodeStatus.SKIPPED:
110
+ if logger:
111
+ logger.warning(f'{step}/{index} was skipped')
112
+ continue
113
+
114
+ has_check = True
115
+
116
+ # Automated checks
117
+ tool = flow.get(step, index, 'tool')
118
+ task = flow.get(step, index, 'task')
119
+
120
+ if metric not in job_data.getkeys("metric"):
121
+ if logger:
122
+ logger.error(f"Criteria must use legal metrics only: {criteria}")
123
+ error = True
124
+ continue
125
+
126
+ if job_data.get("metric", metric, field='type') == 'int':
127
+ goal = int(m.group(3))
128
+ number_format = 'd'
129
+ else:
130
+ goal = float(m.group(3))
131
+
132
+ if goal == 0.0 or (abs(goal) > 1e-3 and abs(goal) < 1e5):
133
+ number_format = '.3f'
134
+ else:
135
+ number_format = '.3e'
136
+
137
+ value = job_data.get('metric', metric, step=step, index=index)
138
+ criteria_ok = utils.safecompare(self, value, op, goal)
139
+ if metric in self.getkeys(item, 'waiver'):
140
+ waivers = self.get(item, 'waiver', metric)
141
+ else:
142
+ waivers = []
143
+
144
+ criteria_str = f'{metric}{op}{goal:{number_format}}'
145
+ compare_str = f'{value:{number_format}}{op}{goal:{number_format}}'
146
+ step_desc = f'job {job} with step {step}/{index} and task {tool}/{task}'
147
+ if not criteria_ok and waivers:
148
+ if logger:
149
+ logger.warning(f'{item} criteria {criteria_str} ({compare_str}) unmet '
150
+ f'by {step_desc}, but found waivers.')
151
+ elif not criteria_ok:
152
+ if logger:
153
+ logger.error(f'{item} criteria {criteria_str} ({compare_str}) unmet '
154
+ f'by {step_desc}.')
155
+ error = True
156
+ elif criteria_ok:
157
+ if logger:
158
+ logger.info(f'{item} criteria {criteria_str} met by {step_desc}.')
159
+
160
+ has_reports = \
161
+ job_data.valid('tool', tool, 'task', task, 'report', metric) and \
162
+ job_data.get('tool', tool, 'task', task, 'report', metric,
163
+ step=step, index=index)
164
+
165
+ if allow_missing_reports and not has_reports:
166
+ # No reports available and it is allowed
167
+ continue
168
+
169
+ reports = []
170
+ try:
171
+ if has_reports:
172
+ reports = job_data.find_files(
173
+ 'tool', tool, 'task', task, 'report', metric,
174
+ step=step, index=index,
175
+ missing_ok=not require_reports)
176
+ except FileNotFoundError:
177
+ reports = []
178
+ continue
179
+
180
+ if require_reports and not reports:
181
+ if logger:
182
+ logger.error(f'No reports generated for metric {metric} in '
183
+ f'{step_desc}')
184
+ error = True
185
+
186
+ for report in reports:
187
+ if not report:
188
+ continue
189
+
190
+ report = os.path.relpath(report, cwd)
191
+ if report not in self.get(item, 'report'):
192
+ self.add(item, 'report', report)
193
+
194
+ if has_check:
195
+ if require_reports and \
196
+ not allow_missing_reports and \
197
+ not self.get(item, 'report'):
198
+ # TODO: validate that report exists?
199
+ if logger:
200
+ logger.error(f'No report documenting item {item}')
201
+ error = True
202
+
203
+ if check_ok and not self.get(item, 'ok'):
204
+ if logger:
205
+ logger.error(f"Item {item} 'ok' field not checked")
206
+ error = True
207
+
208
+ if not error and logger:
209
+ logger.info('Check succeeded!')
210
+
211
+ return not error
212
+
12
213
 
13
214
  ############################################
14
215
  # Design Checklist