siliconcompiler 0.28.3__py3-none-any.whl → 0.28.4__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 (60) hide show
  1. siliconcompiler/_metadata.py +1 -1
  2. siliconcompiler/apps/sc_dashboard.py +1 -1
  3. siliconcompiler/core.py +93 -55
  4. siliconcompiler/fpgas/vpr_example.py +8 -0
  5. siliconcompiler/package.py +3 -2
  6. siliconcompiler/report/dashboard/__init__.py +9 -0
  7. siliconcompiler/report/dashboard/components/__init__.py +13 -1
  8. siliconcompiler/report/dashboard/layouts/vertical_flowgraph.py +4 -3
  9. siliconcompiler/report/dashboard/layouts/vertical_flowgraph_node_tab.py +4 -1
  10. siliconcompiler/report/dashboard/layouts/vertical_flowgraph_sac_tabs.py +4 -1
  11. siliconcompiler/report/dashboard/state.py +3 -1
  12. siliconcompiler/report/summary_table.py +1 -2
  13. siliconcompiler/report/utils.py +1 -2
  14. siliconcompiler/scheduler/__init__.py +2 -0
  15. siliconcompiler/sphinx_ext/dynamicgen.py +6 -0
  16. siliconcompiler/tools/_common/__init__.py +44 -6
  17. siliconcompiler/tools/_common/asic.py +79 -23
  18. siliconcompiler/tools/genfasm/genfasm.py +7 -0
  19. siliconcompiler/tools/ghdl/convert.py +7 -0
  20. siliconcompiler/tools/klayout/convert_drc_db.py +60 -0
  21. siliconcompiler/tools/klayout/drc.py +156 -0
  22. siliconcompiler/tools/klayout/export.py +2 -0
  23. siliconcompiler/tools/klayout/klayout.py +0 -1
  24. siliconcompiler/tools/klayout/klayout_convert_drc_db.py +182 -0
  25. siliconcompiler/tools/klayout/operations.py +2 -0
  26. siliconcompiler/tools/klayout/screenshot.py +2 -0
  27. siliconcompiler/tools/klayout/show.py +4 -4
  28. siliconcompiler/tools/magic/drc.py +21 -0
  29. siliconcompiler/tools/magic/extspice.py +21 -0
  30. siliconcompiler/tools/magic/magic.py +29 -0
  31. siliconcompiler/tools/magic/sc_drc.tcl +2 -12
  32. siliconcompiler/tools/magic/sc_extspice.tcl +3 -15
  33. siliconcompiler/tools/openroad/openroad.py +44 -2
  34. siliconcompiler/tools/openroad/scripts/sc_apr.tcl +15 -0
  35. siliconcompiler/tools/openroad/scripts/sc_floorplan.tcl +55 -9
  36. siliconcompiler/tools/openroad/scripts/sc_metrics.tcl +10 -0
  37. siliconcompiler/tools/openroad/scripts/sc_procs.tcl +3 -1
  38. siliconcompiler/tools/openroad/scripts/sc_route.tcl +8 -2
  39. siliconcompiler/tools/openroad/scripts/sc_screenshot.tcl +0 -5
  40. siliconcompiler/tools/openroad/scripts/sc_write_images.tcl +36 -6
  41. siliconcompiler/tools/surelog/__init__.py +12 -0
  42. siliconcompiler/tools/verilator/compile.py +27 -0
  43. siliconcompiler/tools/verilator/verilator.py +9 -0
  44. siliconcompiler/tools/vpr/vpr.py +18 -0
  45. siliconcompiler/tools/yosys/{syn_asic_fpga_shared.tcl → procs.tcl} +23 -0
  46. siliconcompiler/tools/yosys/sc_screenshot.tcl +104 -0
  47. siliconcompiler/tools/yosys/sc_syn.tcl +7 -9
  48. siliconcompiler/tools/yosys/screenshot.py +153 -0
  49. siliconcompiler/tools/yosys/syn_asic.py +3 -0
  50. siliconcompiler/tools/yosys/syn_asic.tcl +1 -3
  51. siliconcompiler/tools/yosys/syn_fpga.tcl +3 -2
  52. siliconcompiler/toolscripts/_tools.json +3 -3
  53. siliconcompiler/utils/__init__.py +30 -1
  54. siliconcompiler/utils/showtools.py +4 -0
  55. {siliconcompiler-0.28.3.dist-info → siliconcompiler-0.28.4.dist-info}/METADATA +16 -3
  56. {siliconcompiler-0.28.3.dist-info → siliconcompiler-0.28.4.dist-info}/RECORD +60 -55
  57. {siliconcompiler-0.28.3.dist-info → siliconcompiler-0.28.4.dist-info}/WHEEL +1 -1
  58. {siliconcompiler-0.28.3.dist-info → siliconcompiler-0.28.4.dist-info}/LICENSE +0 -0
  59. {siliconcompiler-0.28.3.dist-info → siliconcompiler-0.28.4.dist-info}/entry_points.txt +0 -0
  60. {siliconcompiler-0.28.3.dist-info → siliconcompiler-0.28.4.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  # Version number following semver standard.
2
- version = '0.28.3'
2
+ version = '0.28.4'
3
3
 
4
4
  # Default server address for remote runs, if unspecified.
5
5
  default_server = 'https://server.siliconcompiler.com'
@@ -88,7 +88,7 @@ To include another chip object to compare to:
88
88
  'cfg_path': os.path.abspath(file_path)
89
89
  })
90
90
 
91
- chip._dashboard(wait=True, port=switches['port'], graph_chips=graph_chips)
91
+ chip.dashboard(wait=True, port=switches['port'], graph_chips=graph_chips)
92
92
 
93
93
  return 0
94
94
 
siliconcompiler/core.py CHANGED
@@ -1363,7 +1363,7 @@ class Chip:
1363
1363
  basename = str(pathlib.PurePosixPath(*path_paths[0:n]))
1364
1364
  endname = str(pathlib.PurePosixPath(*path_paths[n:]))
1365
1365
 
1366
- import_name = self.__get_imported_filename(basename, package)
1366
+ import_name = utils.get_hashed_filename(basename, package=package)
1367
1367
  if import_name not in collected_files:
1368
1368
  continue
1369
1369
 
@@ -2051,6 +2051,30 @@ class Chip:
2051
2051
  dot.graph_attr['ranksep'] = '0.75'
2052
2052
  dot.attr(bgcolor=background)
2053
2053
 
2054
+ subgraphs = {
2055
+ "graphs": {
2056
+ "sc-inputs": {
2057
+ "graphs": {},
2058
+ "nodes": []
2059
+ }
2060
+ },
2061
+ "nodes": []
2062
+ }
2063
+ for node, info in nodes.items():
2064
+ if info['is_input']:
2065
+ subgraph_temp = subgraphs["graphs"]["sc-inputs"]
2066
+ else:
2067
+ subgraph_temp = subgraphs
2068
+
2069
+ for key in node.split(".")[0:-1]:
2070
+ if key not in subgraph_temp["graphs"]:
2071
+ subgraph_temp["graphs"][key] = {
2072
+ "graphs": {},
2073
+ "nodes": []
2074
+ }
2075
+ subgraph_temp = subgraph_temp["graphs"][key]
2076
+ subgraph_temp["nodes"].append(node)
2077
+
2054
2078
  with dot.subgraph(name='inputs') as input_graph:
2055
2079
  input_graph.graph_attr['cluster'] = 'true'
2056
2080
  input_graph.graph_attr['color'] = background
@@ -2062,34 +2086,72 @@ class Chip:
2062
2086
  fontcolor=fontcolor, fontsize=fontsize, ordering="in",
2063
2087
  penwidth=penwidth, fillcolor=fillcolor, shape="box")
2064
2088
 
2065
- with dot.subgraph(name='input_nodes') as input_graph_nodes:
2066
- input_graph_nodes.graph_attr['cluster'] = 'true'
2067
- input_graph_nodes.graph_attr['color'] = background
2089
+ def make_node(graph, node, prefix):
2090
+ info = nodes[node]
2068
2091
 
2069
- # add nodes
2070
2092
  shape = "oval" if not show_io else "Mrecord"
2071
- for node, info in nodes.items():
2072
- task_label = f"\\n ({info['task']})" if info['task'] is not None else ""
2073
- if show_io:
2074
- input_labels = [f"<{ikey}> {ifile}" for ifile, ikey in info['inputs'].items()]
2075
- output_labels = [f"<{okey}> {ofile}" for ofile, okey in info['outputs'].items()]
2076
- center_text = f"\\n {node} {task_label} \\n\\n"
2077
- labelname = "{"
2078
- if input_labels:
2079
- labelname += f"{{ {' | '.join(input_labels)} }} |"
2080
- labelname += center_text
2081
- if output_labels:
2082
- labelname += f"| {{ {' | '.join(output_labels)} }}"
2083
- labelname += "}"
2093
+ task_label = f"\\n ({info['task']})" if info['task'] is not None else ""
2094
+ if show_io:
2095
+ input_labels = [f"<{ikey}> {ifile}" for ifile, ikey in info['inputs'].items()]
2096
+ output_labels = [f"<{okey}> {ofile}" for ofile, okey in info['outputs'].items()]
2097
+ center_text = f"\\n {node.replace(prefix, '')} {task_label} \\n\\n"
2098
+ labelname = "{"
2099
+ if input_labels:
2100
+ labelname += f"{{ {' | '.join(input_labels)} }} |"
2101
+ labelname += center_text
2102
+ if output_labels:
2103
+ labelname += f"| {{ {' | '.join(output_labels)} }}"
2104
+ labelname += "}"
2105
+ else:
2106
+ labelname = f"{node.replace(prefix, '')}{task_label}"
2107
+
2108
+ graph.node(node, label=labelname, bordercolor=fontcolor, style='filled',
2109
+ fontcolor=fontcolor, fontsize=fontsize, ordering="in",
2110
+ penwidth=penwidth, fillcolor=fillcolor, shape=shape)
2111
+
2112
+ graph_idx = 0
2113
+
2114
+ def get_node_count(graph_info):
2115
+ nodes = len(graph_info["nodes"])
2116
+
2117
+ for subgraph in graph_info["graphs"]:
2118
+ nodes += get_node_count(graph_info["graphs"][subgraph])
2119
+
2120
+ return nodes
2121
+
2122
+ def build_graph(graph_info, parent, prefix):
2123
+ nonlocal graph_idx
2124
+
2125
+ for subgraph in graph_info["graphs"]:
2126
+ if get_node_count(graph_info["graphs"][subgraph]) > 1:
2127
+ graph = graphviz.Digraph(name=f"cluster_{graph_idx}")
2128
+ graph_idx += 1
2129
+
2130
+ graph.graph_attr['rankdir'] = rankdir
2131
+ graph.attr(bgcolor=background)
2132
+
2133
+ if subgraph == "sc-inputs":
2134
+ graph.attr(style='invis')
2135
+ else:
2136
+ graph.attr(color=fontcolor)
2137
+ graph.attr(style='rounded')
2138
+ graph.attr(shape='oval')
2139
+ graph.attr(label=subgraph)
2140
+ graph.attr(labeljust='l')
2141
+ graph.attr(fontcolor=fontcolor)
2142
+ graph.attr(fontsize=str(int(fontsize) + 2))
2084
2143
  else:
2085
- labelname = f"{node}{task_label}"
2144
+ graph = parent
2145
+
2146
+ build_graph(graph_info["graphs"][subgraph], graph, f"{prefix}{subgraph}.")
2147
+
2148
+ if graph is not parent:
2149
+ parent.subgraph(graph)
2086
2150
 
2087
- dst = dot
2088
- if info['is_input']:
2089
- dst = input_graph_nodes
2090
- dst.node(node, label=labelname, bordercolor=fontcolor, style='filled',
2091
- fontcolor=fontcolor, fontsize=fontsize, ordering="in",
2092
- penwidth=penwidth, fillcolor=fillcolor, shape=shape)
2151
+ for subnode in graph_info["nodes"]:
2152
+ make_node(parent, subnode, prefix)
2153
+
2154
+ build_graph(subgraphs, dot, "")
2093
2155
 
2094
2156
  for edge0, edge1, weight in edges:
2095
2157
  dot.edge(f'{edge0}{out_label_suffix}', f'{edge1}{in_label_suffix}', weight=str(weight))
@@ -2394,7 +2456,7 @@ class Chip:
2394
2456
 
2395
2457
  abspath = dirs[(package, path)]
2396
2458
  if abspath:
2397
- filename = self.__get_imported_filename(posix_path, package)
2459
+ filename = utils.get_hashed_filename(posix_path, package=package)
2398
2460
  dst_path = os.path.join(directory, filename)
2399
2461
  if os.path.exists(dst_path):
2400
2462
  continue
@@ -2466,7 +2528,7 @@ class Chip:
2466
2528
 
2467
2529
  abspath = files[(package, path)]
2468
2530
  if abspath:
2469
- filename = self.__get_imported_filename(posix_path, package)
2531
+ filename = utils.get_hashed_filename(posix_path, package=package)
2470
2532
  dst_path = os.path.join(directory, filename)
2471
2533
  if verbose:
2472
2534
  self.logger.info(f"Copying {abspath} to '{directory}' directory")
@@ -2702,12 +2764,12 @@ class Chip:
2702
2764
  return hashlist
2703
2765
 
2704
2766
  ###########################################################################
2705
- def _dashboard(self, wait=True, port=None, graph_chips=None):
2767
+ def dashboard(self, wait=True, port=None, graph_chips=None):
2706
2768
  '''
2707
2769
  Open a session of the dashboard.
2708
2770
 
2709
2771
  The dashboard can be viewed in any webbrowser and can be accessed via:
2710
- http://localhost:8501/
2772
+ http://localhost:<port>/
2711
2773
 
2712
2774
  Args:
2713
2775
  wait (bool): If True, this call will wait in this method
@@ -2718,7 +2780,7 @@ class Chip:
2718
2780
  {'chip': chip object, 'name': chip name}
2719
2781
 
2720
2782
  Examples:
2721
- >>> chip._dashboard()
2783
+ >>> chip.dashboard()
2722
2784
  Opens a sesison of the dashboard.
2723
2785
  '''
2724
2786
  if self._dash:
@@ -3143,6 +3205,7 @@ class Chip:
3143
3205
  self.set('option', 'nodisplay', False, clobber=True)
3144
3206
  self.set('option', 'continue', True, clobber=True)
3145
3207
  self.set('option', 'quiet', False, clobber=True)
3208
+ self.set('option', 'clean', True, clobber=True)
3146
3209
  self.set('arg', 'step', None, clobber=True)
3147
3210
  self.set('arg', 'index', None, clobber=True)
3148
3211
  self.unset('option', 'to')
@@ -3237,31 +3300,6 @@ class Chip:
3237
3300
 
3238
3301
  return os.path.join(*dirlist)
3239
3302
 
3240
- #######################################
3241
- def __get_imported_filename(self, pathstr, package=None):
3242
- ''' Utility to map collected file to an unambiguous name based on its path.
3243
-
3244
- The mapping looks like:
3245
- path/to/file.ext => file_<md5('path/to/file.ext')>.ext
3246
- '''
3247
- path = pathlib.PurePosixPath(pathstr)
3248
- ext = ''.join(path.suffixes)
3249
-
3250
- # strip off all file suffixes to get just the bare name
3251
- barepath = path
3252
- while barepath.suffix:
3253
- barepath = pathlib.PurePosixPath(barepath.stem)
3254
- filename = str(barepath.parts[-1])
3255
-
3256
- if not package:
3257
- package = ''
3258
- else:
3259
- package = f'{package}:'
3260
- path_to_hash = f'{package}{str(path)}'
3261
- pathhash = hashlib.sha1(path_to_hash.encode('utf-8')).hexdigest()
3262
-
3263
- return f'{filename}_{pathhash}{ext}'
3264
-
3265
3303
  def error(self, msg):
3266
3304
  '''
3267
3305
  Raises error.
@@ -43,6 +43,14 @@ def setup():
43
43
 
44
44
  fpga.set('fpga', part_name, 'vendor', vendor)
45
45
 
46
+ # Part name is specified per architecture file. Device code specifies
47
+ # which <fixed_layout> name to use when running VPR. These examples
48
+ # use the following names:
49
+ if (part_name == 'example_arch_X005Y005'):
50
+ fpga.set('fpga', part_name, 'var', 'vpr_device_code', 'fpga_beta')
51
+ else:
52
+ fpga.set('fpga', part_name, 'var', 'vpr_device_code', part_name)
53
+
46
54
  fpga.set('fpga', part_name, 'lutsize', lut_size)
47
55
 
48
56
  arch_root = os.path.join(flow_root, 'arch', part_name)
@@ -88,7 +88,8 @@ def _path(chip, package, download_handler):
88
88
  chip._packages[package] = data_path
89
89
  return data_path
90
90
 
91
- raise SiliconCompilerError(f'Extracting {package} data to {data_path} failed')
91
+ raise SiliconCompilerError(f'Extracting {package} data to {data_path} failed',
92
+ chip=chip)
92
93
 
93
94
 
94
95
  def path(chip, package):
@@ -188,7 +189,7 @@ def clone_synchronized(chip, package, data, data_path):
188
189
  elif url.scheme in ['git', 'git+https']:
189
190
  chip.logger.error('Failed to authenticate. Please use a token or ssh.')
190
191
  else:
191
- raise e
192
+ chip.logger.error(str(e))
192
193
 
193
194
 
194
195
  def clone_from_git(chip, package, data, repo_path):
@@ -12,6 +12,7 @@ import atexit
12
12
  import shutil
13
13
  import fasteners
14
14
  import signal
15
+ import socketserver
15
16
 
16
17
  from siliconcompiler.report.dashboard import utils
17
18
 
@@ -25,6 +26,8 @@ class Dashboard():
25
26
  pass
26
27
 
27
28
  def __init__(self, chip, port=None, graph_chips=None):
29
+ if not port:
30
+ port = Dashboard.get_next_port()
28
31
  if not port:
29
32
  port = Dashboard.__port
30
33
 
@@ -172,3 +175,9 @@ class Dashboard():
172
175
 
173
176
  if os.path.exists(self.__directory):
174
177
  shutil.rmtree(self.__directory)
178
+
179
+ @staticmethod
180
+ def get_next_port():
181
+ with socketserver.TCPServer(("localhost", 0), None) as s:
182
+ return s.server_address[1]
183
+ return None
@@ -194,7 +194,7 @@ def setup_page():
194
194
  menu_items=SC_MENU)
195
195
 
196
196
 
197
- def file_viewer(chip, path, header_col_width=0.89):
197
+ def file_viewer(chip, path, page_key=None, header_col_width=0.89):
198
198
  if not path:
199
199
  streamlit.error('Select a file')
200
200
  return
@@ -243,14 +243,25 @@ def file_viewer(chip, path, header_col_width=0.89):
243
243
 
244
244
  file_section = streamlit.container()
245
245
 
246
+ if page_key:
247
+ if state.get_key(page_key) is None:
248
+ state.set_key(page_key, 1)
249
+ index = state.get_key(page_key)
250
+ else:
251
+ index = 1
252
+
246
253
  page = sac.pagination(
247
254
  align='center',
255
+ index=index,
248
256
  jump=True,
249
257
  show_total=True,
250
258
  page_size=page_size,
251
259
  total=max_pages,
252
260
  disabled=max_pages < state.get_key(state.MAX_FILE_LINES_TO_SHOW))
253
261
 
262
+ if page_key:
263
+ state.set_key(page_key, page)
264
+
254
265
  start_idx = (page - 1) * state.get_key(state.MAX_FILE_LINES_TO_SHOW)
255
266
  end_idx = start_idx + state.get_key(state.MAX_FILE_LINES_TO_SHOW)
256
267
  file_show = file_data[start_idx:end_idx]
@@ -465,6 +476,7 @@ def node_file_tree_viewer(chip, step, index):
465
476
 
466
477
  if selected and os.path.isfile(selected):
467
478
  state.set_key(state.SELECTED_FILE, selected)
479
+ state.set_key(state.SELECTED_FILE_PAGE, None)
468
480
 
469
481
 
470
482
  def node_viewer(chip, step, index, metric_dataframe, height=None):
@@ -80,9 +80,10 @@ def layout():
80
80
  components.manifest_viewer(chip)
81
81
 
82
82
  with tabs["File Viewer"]:
83
- path = state.get_key(state.SELECTED_FILE)
84
-
85
- components.file_viewer(chip, path)
83
+ components.file_viewer(
84
+ chip,
85
+ state.get_key(state.SELECTED_FILE),
86
+ page_key=state.SELECTED_FILE_PAGE)
86
87
 
87
88
  if "Design Preview" in tabs:
88
89
  with tabs["Design Preview"]:
@@ -103,7 +103,10 @@ def layout():
103
103
  components.manifest_viewer(chip)
104
104
 
105
105
  if tab_selected == "File Viewer":
106
- components.file_viewer(chip, state.get_key(state.SELECTED_FILE))
106
+ components.file_viewer(
107
+ chip,
108
+ state.get_key(state.SELECTED_FILE),
109
+ page_key=state.SELECTED_FILE_PAGE)
107
110
 
108
111
  if tab_selected == "Design Preview":
109
112
  components.file_viewer(chip, f'{chip.getworkdir()}/{chip.design}.png')
@@ -96,7 +96,10 @@ def layout():
96
96
  components.manifest_viewer(chip)
97
97
 
98
98
  if tab_selected == "File Viewer":
99
- components.file_viewer(chip, state.get_key(state.SELECTED_FILE))
99
+ components.file_viewer(
100
+ chip,
101
+ state.get_key(state.SELECTED_FILE),
102
+ page_key=state.SELECTED_FILE_PAGE)
100
103
 
101
104
  if tab_selected == "Design Preview":
102
105
  components.file_viewer(chip, f'{chip.getworkdir()}/{chip.design}.png')
@@ -16,6 +16,7 @@ SELECTED_FLOWGRAPH_NODE = "selected_flowgraph_node"
16
16
  SELECTED_SELECTOR_NODE = "selected_selector_node"
17
17
  NODE_SOURCE = "node_source"
18
18
  SELECTED_FILE = "selected_file"
19
+ SELECTED_FILE_PAGE = "selected_file_page"
19
20
  LOADED_CHIPS = "loaded_chips"
20
21
  UI_WIDTH = "ui_width"
21
22
  MANIFEST_FILE = "manifest_file"
@@ -73,6 +74,7 @@ def init():
73
74
  _add_default(SELECTED_SELECTOR_NODE, None)
74
75
  _add_default(NODE_SOURCE, None)
75
76
  _add_default(SELECTED_FILE, None)
77
+ _add_default(SELECTED_FILE_PAGE, None)
76
78
  _add_default(LOADED_CHIPS, {})
77
79
  _add_default(MANIFEST_FILE, None)
78
80
  _add_default(MANIFEST_LOCK, None)
@@ -80,7 +82,7 @@ def init():
80
82
  _add_default(IS_RUNNING, False)
81
83
  _add_default(GRAPH_JOBS, None)
82
84
  _add_default(UI_WIDTH, None)
83
- _add_default(APP_LAYOUT, "vertical_flowgraph")
85
+ _add_default(APP_LAYOUT, "vertical_flowgraph_sac_tabs")
84
86
  _add_default(APP_RERUN, None)
85
87
  _add_default(APP_RUNNING_REFRESH, 2 * 1000)
86
88
  _add_default(APP_STOPPED_REFRESH, 30 * 1000)
@@ -12,10 +12,9 @@ def _show_summary_table(chip, flow, flowgraph_nodes, show_all_indices):
12
12
  '''
13
13
 
14
14
  # Display data
15
- max_line_width = 135
16
15
  column_width = 15
17
16
 
18
- max_line_width = max(max_line_width, int(0.9*shutil.get_terminal_size((80, 20)).columns))
17
+ max_line_width = max(2 * column_width, int(0.95*shutil.get_terminal_size().columns))
19
18
 
20
19
  nodes, _, metrics, metrics_unit, metrics_to_show, _ = \
21
20
  _collect_data(chip, flow, flowgraph_nodes)
@@ -143,8 +143,7 @@ def _get_flowgraph_path(chip, flow, nodes_to_execute, only_include_successful=Fa
143
143
  end_nodes = _get_flowgraph_exit_nodes(chip, flow, steps=flowgraph_steps)
144
144
  for node in end_nodes:
145
145
  if only_include_successful:
146
- if chip.get('record', 'status', step=node[0], index=node[1]) == \
147
- NodeStatus.SUCCESS:
146
+ if NodeStatus.is_success(chip.get('record', 'status', step=node[0], index=node[1])):
148
147
  selected_nodes.add(node)
149
148
  to_search.append(node)
150
149
  else:
@@ -898,6 +898,7 @@ def _run_executable_or_builtin(chip, step, index, version, toolpath, workdir, ru
898
898
 
899
899
  cmd_start_time = time.time()
900
900
  proc = subprocess.Popen(cmdlist,
901
+ stdin=subprocess.DEVNULL,
901
902
  stdout=stdout_writer,
902
903
  stderr=stderr_writer,
903
904
  preexec_fn=preexec_fn)
@@ -1118,6 +1119,7 @@ def _check_tool_version(chip, step, index, run_func=None):
1118
1119
  cmdlist = [exe]
1119
1120
  cmdlist.extend(veropt)
1120
1121
  proc = subprocess.run(cmdlist,
1122
+ stdin=subprocess.DEVNULL,
1121
1123
  stdout=subprocess.PIPE,
1122
1124
  stderr=subprocess.STDOUT,
1123
1125
  universal_newlines=True)
@@ -343,6 +343,8 @@ class DynamicGen(SphinxDirective):
343
343
  type_heading = "Variables"
344
344
  elif type == "file":
345
345
  type_heading = "Files"
346
+ elif type == "dir":
347
+ type_heading = "Directories"
346
348
 
347
349
  table = [[strong('Parameters'), strong('Help')]]
348
350
  for key, params in cfg.items():
@@ -657,6 +659,9 @@ class ToolGen(DynamicGen):
657
659
  '''Display config under `eda, <modname>` in a single table.'''
658
660
  cfg = chip.getdict('tool', toolname, 'task', taskname)
659
661
  schema = Schema(cfg=cfg)
662
+ for vals, step, index in schema._getvals('require'):
663
+ schema.set('require', sorted(set(vals)),
664
+ step=step, index=index)
660
665
  schema.prune()
661
666
  pruned = schema.cfg
662
667
  table = build_schema_value_table(pruned, self.env.docname,
@@ -789,6 +794,7 @@ class ToolGen(DynamicGen):
789
794
  key_path = ['tool', '<tool>', 'task', '<task>']
790
795
  self._document_free_params(cfg, 'var', key_path + ['var'], reference_prefix, s)
791
796
  self._document_free_params(cfg, 'file', key_path + ['file'], reference_prefix, s)
797
+ self._document_free_params(cfg, 'dir', key_path + ['dir'], reference_prefix, s)
792
798
 
793
799
  def _handle_setup(self, chip, module):
794
800
  setup = self.get_setup_method(module)
@@ -2,7 +2,7 @@ import os
2
2
  import pkgutil
3
3
 
4
4
 
5
- def get_libraries(chip, include_asic=True, library=None, libraries=None):
5
+ def __get_library_keys(chip, include_asic=True, library=None, libraries=None):
6
6
  '''
7
7
  Returns a list of libraries included in this step/index
8
8
 
@@ -24,18 +24,52 @@ def get_libraries(chip, include_asic=True, library=None, libraries=None):
24
24
 
25
25
  def get_libs(*key):
26
26
  if chip.valid(*key) and chip.get(*key, step=step, index=index):
27
- return chip.get(*key, step=step, index=index)
27
+ if chip.get(*key, step=step, index=index):
28
+ return [key]
28
29
  return []
29
30
 
30
31
  if include_asic:
31
32
  libs.extend(get_libs(*pref_key, 'asic', 'logiclib'))
32
33
  libs.extend(get_libs(*pref_key, 'asic', 'macrolib'))
33
34
 
34
- for lib in get_libs(*pref_key, 'option', 'library'):
35
- if lib in libs or lib in libraries:
35
+ libnames = set()
36
+ if library:
37
+ libnames.add(library)
38
+ for lib_key in get_libs(*pref_key, 'option', 'library'):
39
+ if lib_key in libs:
36
40
  continue
37
- libs.append(lib)
38
- libs.extend(get_libraries(chip, include_asic=include_asic, library=lib, libraries=libs))
41
+
42
+ libs.append(lib_key)
43
+
44
+ for libname in chip.get(*lib_key, step=step, index=index):
45
+ if libname in libnames:
46
+ continue
47
+
48
+ libnames.add(libname)
49
+ libs.extend(__get_library_keys(
50
+ chip,
51
+ include_asic=include_asic,
52
+ library=libname,
53
+ libraries=libnames))
54
+
55
+ return set(libs)
56
+
57
+
58
+ def get_libraries(chip, include_asic=True):
59
+ '''
60
+ Returns a list of libraries included in this step/index
61
+
62
+ Args:
63
+ chip (Chip): Chip object
64
+ include_asic (bool): include the asic libraries.
65
+ '''
66
+ step = chip.get('arg', 'step')
67
+ index = chip.get('arg', 'index')
68
+
69
+ libs = []
70
+
71
+ for key in __get_library_keys(chip, include_asic=include_asic):
72
+ libs.extend(chip.get(*key, step=step, index=index))
39
73
 
40
74
  return set(libs)
41
75
 
@@ -212,6 +246,10 @@ def add_frontend_requires(chip, supports=None):
212
246
  step = chip.get('arg', 'step')
213
247
  index = chip.get('arg', 'index')
214
248
  tool, task = get_tool_task(chip, step, index)
249
+
250
+ for libkey in __get_library_keys(chip):
251
+ chip.add('tool', tool, 'task', task, 'require', ','.join(libkey), step=step, index=index)
252
+
215
253
  for opt in supports:
216
254
  for key in opt_keys[opt]:
217
255
  chip.add('tool', tool, 'task', task, 'require', ','.join(key), step=step, index=index)