siliconcompiler 0.26.5__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 (251) hide show
  1. siliconcompiler/__init__.py +24 -0
  2. siliconcompiler/__main__.py +12 -0
  3. siliconcompiler/_common.py +49 -0
  4. siliconcompiler/_metadata.py +36 -0
  5. siliconcompiler/apps/__init__.py +0 -0
  6. siliconcompiler/apps/_common.py +76 -0
  7. siliconcompiler/apps/sc.py +92 -0
  8. siliconcompiler/apps/sc_dashboard.py +94 -0
  9. siliconcompiler/apps/sc_issue.py +178 -0
  10. siliconcompiler/apps/sc_remote.py +199 -0
  11. siliconcompiler/apps/sc_server.py +39 -0
  12. siliconcompiler/apps/sc_show.py +142 -0
  13. siliconcompiler/apps/smake.py +232 -0
  14. siliconcompiler/checklists/__init__.py +0 -0
  15. siliconcompiler/checklists/oh_tapeout.py +41 -0
  16. siliconcompiler/core.py +3221 -0
  17. siliconcompiler/data/RobotoMono/LICENSE.txt +202 -0
  18. siliconcompiler/data/RobotoMono/RobotoMono-Regular.ttf +0 -0
  19. siliconcompiler/data/heartbeat.v +18 -0
  20. siliconcompiler/data/logo.png +0 -0
  21. siliconcompiler/flowgraph.py +570 -0
  22. siliconcompiler/flows/__init__.py +0 -0
  23. siliconcompiler/flows/_common.py +67 -0
  24. siliconcompiler/flows/asicflow.py +180 -0
  25. siliconcompiler/flows/asictopflow.py +38 -0
  26. siliconcompiler/flows/dvflow.py +86 -0
  27. siliconcompiler/flows/fpgaflow.py +202 -0
  28. siliconcompiler/flows/generate_openroad_rcx.py +66 -0
  29. siliconcompiler/flows/lintflow.py +35 -0
  30. siliconcompiler/flows/screenshotflow.py +51 -0
  31. siliconcompiler/flows/showflow.py +59 -0
  32. siliconcompiler/flows/signoffflow.py +53 -0
  33. siliconcompiler/flows/synflow.py +128 -0
  34. siliconcompiler/fpgas/__init__.py +0 -0
  35. siliconcompiler/fpgas/lattice_ice40.py +42 -0
  36. siliconcompiler/fpgas/vpr_example.py +109 -0
  37. siliconcompiler/issue.py +300 -0
  38. siliconcompiler/libs/__init__.py +0 -0
  39. siliconcompiler/libs/asap7sc7p5t.py +8 -0
  40. siliconcompiler/libs/gf180mcu.py +8 -0
  41. siliconcompiler/libs/nangate45.py +8 -0
  42. siliconcompiler/libs/sky130hd.py +8 -0
  43. siliconcompiler/libs/sky130io.py +8 -0
  44. siliconcompiler/package.py +412 -0
  45. siliconcompiler/pdks/__init__.py +0 -0
  46. siliconcompiler/pdks/asap7.py +8 -0
  47. siliconcompiler/pdks/freepdk45.py +8 -0
  48. siliconcompiler/pdks/gf180.py +8 -0
  49. siliconcompiler/pdks/skywater130.py +8 -0
  50. siliconcompiler/remote/__init__.py +36 -0
  51. siliconcompiler/remote/client.py +891 -0
  52. siliconcompiler/remote/schema.py +106 -0
  53. siliconcompiler/remote/server.py +507 -0
  54. siliconcompiler/remote/server_schema/requests/cancel_job.json +51 -0
  55. siliconcompiler/remote/server_schema/requests/check_progress.json +61 -0
  56. siliconcompiler/remote/server_schema/requests/check_server.json +38 -0
  57. siliconcompiler/remote/server_schema/requests/delete_job.json +51 -0
  58. siliconcompiler/remote/server_schema/requests/get_results.json +48 -0
  59. siliconcompiler/remote/server_schema/requests/remote_run.json +40 -0
  60. siliconcompiler/remote/server_schema/responses/cancel_job.json +18 -0
  61. siliconcompiler/remote/server_schema/responses/check_progress.json +30 -0
  62. siliconcompiler/remote/server_schema/responses/check_server.json +32 -0
  63. siliconcompiler/remote/server_schema/responses/delete_job.json +18 -0
  64. siliconcompiler/remote/server_schema/responses/get_results.json +21 -0
  65. siliconcompiler/remote/server_schema/responses/remote_run.json +25 -0
  66. siliconcompiler/report/__init__.py +13 -0
  67. siliconcompiler/report/html_report.py +74 -0
  68. siliconcompiler/report/report.py +355 -0
  69. siliconcompiler/report/streamlit_report.py +137 -0
  70. siliconcompiler/report/streamlit_viewer.py +944 -0
  71. siliconcompiler/report/summary_image.py +117 -0
  72. siliconcompiler/report/summary_table.py +105 -0
  73. siliconcompiler/report/utils.py +163 -0
  74. siliconcompiler/scheduler/__init__.py +2092 -0
  75. siliconcompiler/scheduler/docker_runner.py +253 -0
  76. siliconcompiler/scheduler/run_node.py +138 -0
  77. siliconcompiler/scheduler/send_messages.py +178 -0
  78. siliconcompiler/scheduler/slurm.py +208 -0
  79. siliconcompiler/scheduler/validation/email_credentials.json +54 -0
  80. siliconcompiler/schema/__init__.py +7 -0
  81. siliconcompiler/schema/schema_cfg.py +4014 -0
  82. siliconcompiler/schema/schema_obj.py +1841 -0
  83. siliconcompiler/schema/utils.py +93 -0
  84. siliconcompiler/sphinx_ext/__init__.py +0 -0
  85. siliconcompiler/sphinx_ext/dynamicgen.py +1006 -0
  86. siliconcompiler/sphinx_ext/schemagen.py +221 -0
  87. siliconcompiler/sphinx_ext/utils.py +166 -0
  88. siliconcompiler/targets/__init__.py +0 -0
  89. siliconcompiler/targets/asap7_demo.py +68 -0
  90. siliconcompiler/targets/asic_demo.py +38 -0
  91. siliconcompiler/targets/fpgaflow_demo.py +47 -0
  92. siliconcompiler/targets/freepdk45_demo.py +59 -0
  93. siliconcompiler/targets/gf180_demo.py +77 -0
  94. siliconcompiler/targets/skywater130_demo.py +70 -0
  95. siliconcompiler/templates/email/general.j2 +66 -0
  96. siliconcompiler/templates/email/summary.j2 +43 -0
  97. siliconcompiler/templates/issue/README.txt +26 -0
  98. siliconcompiler/templates/issue/run.sh +6 -0
  99. siliconcompiler/templates/report/bootstrap.min.css +7 -0
  100. siliconcompiler/templates/report/bootstrap.min.js +7 -0
  101. siliconcompiler/templates/report/bootstrap_LICENSE.md +24 -0
  102. siliconcompiler/templates/report/sc_report.j2 +427 -0
  103. siliconcompiler/templates/slurm/run.sh +9 -0
  104. siliconcompiler/templates/tcl/manifest.tcl.j2 +137 -0
  105. siliconcompiler/tools/__init__.py +0 -0
  106. siliconcompiler/tools/_common/__init__.py +432 -0
  107. siliconcompiler/tools/_common/asic.py +115 -0
  108. siliconcompiler/tools/_common/sdc/sc_constraints.sdc +76 -0
  109. siliconcompiler/tools/_common/tcl/sc_pin_constraints.tcl +63 -0
  110. siliconcompiler/tools/bambu/bambu.py +32 -0
  111. siliconcompiler/tools/bambu/convert.py +77 -0
  112. siliconcompiler/tools/bluespec/bluespec.py +40 -0
  113. siliconcompiler/tools/bluespec/convert.py +103 -0
  114. siliconcompiler/tools/builtin/_common.py +155 -0
  115. siliconcompiler/tools/builtin/builtin.py +26 -0
  116. siliconcompiler/tools/builtin/concatenate.py +85 -0
  117. siliconcompiler/tools/builtin/join.py +27 -0
  118. siliconcompiler/tools/builtin/maximum.py +46 -0
  119. siliconcompiler/tools/builtin/minimum.py +57 -0
  120. siliconcompiler/tools/builtin/mux.py +70 -0
  121. siliconcompiler/tools/builtin/nop.py +38 -0
  122. siliconcompiler/tools/builtin/verify.py +83 -0
  123. siliconcompiler/tools/chisel/SCDriver.scala +10 -0
  124. siliconcompiler/tools/chisel/build.sbt +27 -0
  125. siliconcompiler/tools/chisel/chisel.py +37 -0
  126. siliconcompiler/tools/chisel/convert.py +140 -0
  127. siliconcompiler/tools/execute/exec_input.py +41 -0
  128. siliconcompiler/tools/execute/execute.py +17 -0
  129. siliconcompiler/tools/genfasm/bitstream.py +61 -0
  130. siliconcompiler/tools/genfasm/genfasm.py +40 -0
  131. siliconcompiler/tools/ghdl/convert.py +87 -0
  132. siliconcompiler/tools/ghdl/ghdl.py +41 -0
  133. siliconcompiler/tools/icarus/compile.py +87 -0
  134. siliconcompiler/tools/icarus/icarus.py +36 -0
  135. siliconcompiler/tools/icepack/bitstream.py +20 -0
  136. siliconcompiler/tools/icepack/icepack.py +43 -0
  137. siliconcompiler/tools/klayout/export.py +117 -0
  138. siliconcompiler/tools/klayout/klayout.py +119 -0
  139. siliconcompiler/tools/klayout/klayout_export.py +205 -0
  140. siliconcompiler/tools/klayout/klayout_operations.py +363 -0
  141. siliconcompiler/tools/klayout/klayout_show.py +242 -0
  142. siliconcompiler/tools/klayout/klayout_utils.py +176 -0
  143. siliconcompiler/tools/klayout/operations.py +194 -0
  144. siliconcompiler/tools/klayout/screenshot.py +98 -0
  145. siliconcompiler/tools/klayout/show.py +101 -0
  146. siliconcompiler/tools/magic/drc.py +49 -0
  147. siliconcompiler/tools/magic/extspice.py +19 -0
  148. siliconcompiler/tools/magic/magic.py +85 -0
  149. siliconcompiler/tools/magic/sc_drc.tcl +96 -0
  150. siliconcompiler/tools/magic/sc_extspice.tcl +54 -0
  151. siliconcompiler/tools/magic/sc_magic.tcl +47 -0
  152. siliconcompiler/tools/montage/montage.py +30 -0
  153. siliconcompiler/tools/montage/tile.py +66 -0
  154. siliconcompiler/tools/netgen/count_lvs.py +132 -0
  155. siliconcompiler/tools/netgen/lvs.py +90 -0
  156. siliconcompiler/tools/netgen/netgen.py +36 -0
  157. siliconcompiler/tools/netgen/sc_lvs.tcl +46 -0
  158. siliconcompiler/tools/nextpnr/apr.py +24 -0
  159. siliconcompiler/tools/nextpnr/nextpnr.py +59 -0
  160. siliconcompiler/tools/openfpgaloader/openfpgaloader.py +39 -0
  161. siliconcompiler/tools/openroad/__init__.py +0 -0
  162. siliconcompiler/tools/openroad/cts.py +45 -0
  163. siliconcompiler/tools/openroad/dfm.py +66 -0
  164. siliconcompiler/tools/openroad/export.py +131 -0
  165. siliconcompiler/tools/openroad/floorplan.py +70 -0
  166. siliconcompiler/tools/openroad/openroad.py +977 -0
  167. siliconcompiler/tools/openroad/physyn.py +27 -0
  168. siliconcompiler/tools/openroad/place.py +41 -0
  169. siliconcompiler/tools/openroad/rcx_bench.py +95 -0
  170. siliconcompiler/tools/openroad/rcx_extract.py +34 -0
  171. siliconcompiler/tools/openroad/route.py +45 -0
  172. siliconcompiler/tools/openroad/screenshot.py +60 -0
  173. siliconcompiler/tools/openroad/scripts/sc_apr.tcl +499 -0
  174. siliconcompiler/tools/openroad/scripts/sc_cts.tcl +64 -0
  175. siliconcompiler/tools/openroad/scripts/sc_dfm.tcl +20 -0
  176. siliconcompiler/tools/openroad/scripts/sc_export.tcl +98 -0
  177. siliconcompiler/tools/openroad/scripts/sc_floorplan.tcl +413 -0
  178. siliconcompiler/tools/openroad/scripts/sc_metrics.tcl +158 -0
  179. siliconcompiler/tools/openroad/scripts/sc_physyn.tcl +7 -0
  180. siliconcompiler/tools/openroad/scripts/sc_place.tcl +84 -0
  181. siliconcompiler/tools/openroad/scripts/sc_procs.tcl +423 -0
  182. siliconcompiler/tools/openroad/scripts/sc_rcx.tcl +63 -0
  183. siliconcompiler/tools/openroad/scripts/sc_rcx_bench.tcl +20 -0
  184. siliconcompiler/tools/openroad/scripts/sc_rcx_extract.tcl +12 -0
  185. siliconcompiler/tools/openroad/scripts/sc_route.tcl +133 -0
  186. siliconcompiler/tools/openroad/scripts/sc_screenshot.tcl +21 -0
  187. siliconcompiler/tools/openroad/scripts/sc_write.tcl +5 -0
  188. siliconcompiler/tools/openroad/scripts/sc_write_images.tcl +361 -0
  189. siliconcompiler/tools/openroad/show.py +94 -0
  190. siliconcompiler/tools/openroad/templates/pex.tcl +8 -0
  191. siliconcompiler/tools/opensta/__init__.py +101 -0
  192. siliconcompiler/tools/opensta/report_libraries.py +28 -0
  193. siliconcompiler/tools/opensta/scripts/sc_procs.tcl +47 -0
  194. siliconcompiler/tools/opensta/scripts/sc_report_libraries.tcl +74 -0
  195. siliconcompiler/tools/opensta/scripts/sc_timing.tcl +268 -0
  196. siliconcompiler/tools/opensta/timing.py +214 -0
  197. siliconcompiler/tools/slang/__init__.py +49 -0
  198. siliconcompiler/tools/slang/lint.py +101 -0
  199. siliconcompiler/tools/surelog/__init__.py +123 -0
  200. siliconcompiler/tools/surelog/parse.py +183 -0
  201. siliconcompiler/tools/surelog/templates/output.v +7 -0
  202. siliconcompiler/tools/sv2v/convert.py +46 -0
  203. siliconcompiler/tools/sv2v/sv2v.py +37 -0
  204. siliconcompiler/tools/template/template.py +125 -0
  205. siliconcompiler/tools/verilator/compile.py +139 -0
  206. siliconcompiler/tools/verilator/lint.py +19 -0
  207. siliconcompiler/tools/verilator/parse.py +27 -0
  208. siliconcompiler/tools/verilator/verilator.py +172 -0
  209. siliconcompiler/tools/vivado/__init__.py +7 -0
  210. siliconcompiler/tools/vivado/bitstream.py +21 -0
  211. siliconcompiler/tools/vivado/place.py +21 -0
  212. siliconcompiler/tools/vivado/route.py +21 -0
  213. siliconcompiler/tools/vivado/scripts/sc_bitstream.tcl +6 -0
  214. siliconcompiler/tools/vivado/scripts/sc_place.tcl +2 -0
  215. siliconcompiler/tools/vivado/scripts/sc_route.tcl +4 -0
  216. siliconcompiler/tools/vivado/scripts/sc_run.tcl +45 -0
  217. siliconcompiler/tools/vivado/scripts/sc_syn_fpga.tcl +25 -0
  218. siliconcompiler/tools/vivado/syn_fpga.py +20 -0
  219. siliconcompiler/tools/vivado/vivado.py +147 -0
  220. siliconcompiler/tools/vpr/_json_constraint.py +63 -0
  221. siliconcompiler/tools/vpr/_xml_constraint.py +109 -0
  222. siliconcompiler/tools/vpr/place.py +137 -0
  223. siliconcompiler/tools/vpr/route.py +124 -0
  224. siliconcompiler/tools/vpr/screenshot.py +54 -0
  225. siliconcompiler/tools/vpr/show.py +88 -0
  226. siliconcompiler/tools/vpr/vpr.py +357 -0
  227. siliconcompiler/tools/xyce/xyce.py +36 -0
  228. siliconcompiler/tools/yosys/lec.py +56 -0
  229. siliconcompiler/tools/yosys/prepareLib.py +59 -0
  230. siliconcompiler/tools/yosys/sc_lec.tcl +84 -0
  231. siliconcompiler/tools/yosys/sc_syn.tcl +79 -0
  232. siliconcompiler/tools/yosys/syn_asic.py +565 -0
  233. siliconcompiler/tools/yosys/syn_asic.tcl +377 -0
  234. siliconcompiler/tools/yosys/syn_asic_fpga_shared.tcl +31 -0
  235. siliconcompiler/tools/yosys/syn_fpga.py +146 -0
  236. siliconcompiler/tools/yosys/syn_fpga.tcl +233 -0
  237. siliconcompiler/tools/yosys/syn_strategies.tcl +81 -0
  238. siliconcompiler/tools/yosys/techmaps/lcu_kogge_stone.v +39 -0
  239. siliconcompiler/tools/yosys/templates/abc.const +2 -0
  240. siliconcompiler/tools/yosys/yosys.py +147 -0
  241. siliconcompiler/units.py +259 -0
  242. siliconcompiler/use.py +177 -0
  243. siliconcompiler/utils/__init__.py +423 -0
  244. siliconcompiler/utils/asic.py +158 -0
  245. siliconcompiler/utils/showtools.py +25 -0
  246. siliconcompiler-0.26.5.dist-info/LICENSE +190 -0
  247. siliconcompiler-0.26.5.dist-info/METADATA +195 -0
  248. siliconcompiler-0.26.5.dist-info/RECORD +251 -0
  249. siliconcompiler-0.26.5.dist-info/WHEEL +5 -0
  250. siliconcompiler-0.26.5.dist-info/entry_points.txt +12 -0
  251. siliconcompiler-0.26.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1006 @@
1
+ '''Sphinx extension that provides directives for automatically generating
2
+ documentation for dynamically loaded modules used by SC.
3
+ '''
4
+
5
+ from docutils import nodes
6
+ from sphinx.util.nodes import nested_parse_with_titles
7
+ from docutils.statemachine import ViewList
8
+ from sphinx.util.docutils import SphinxDirective
9
+ from sphinx.domains.std import StandardDomain
10
+ from sphinx.addnodes import pending_xref
11
+ import docutils
12
+
13
+ import importlib
14
+ import pkgutil
15
+ import os
16
+ import subprocess
17
+
18
+ import siliconcompiler
19
+ from siliconcompiler.schema import Schema, utils
20
+ from siliconcompiler.sphinx_ext.utils import (
21
+ strong,
22
+ code,
23
+ para,
24
+ keypath,
25
+ build_table,
26
+ build_list,
27
+ build_section,
28
+ build_section_with_target,
29
+ link,
30
+ image,
31
+ get_ref_id,
32
+ literalblock
33
+ )
34
+
35
+ #############
36
+ # Helpers
37
+ #############
38
+
39
+ # We need this in a few places, so just make it global
40
+ SC_ROOT = os.path.abspath(f'{__file__}/../../../')
41
+
42
+
43
+ def build_schema_value_table(cfg, refdoc, keypath_prefix=None, skip_zero_weight=False):
44
+ '''Helper function for displaying values set in schema as a docutils table.'''
45
+ table = [[strong('Keypath'), strong('Value')]]
46
+
47
+ # Nest received dictionary under keypath_prefix
48
+ rooted_cfg = cfg
49
+ if keypath_prefix:
50
+ for key in reversed(keypath_prefix):
51
+ rooted_cfg = {key: rooted_cfg}
52
+
53
+ def format_value(is_list, value):
54
+ if is_list:
55
+ if len(value) > 1:
56
+ val_node = build_list([code(v) for v in value])
57
+ elif len(value) > 0:
58
+ val_node = code(value[0])
59
+ else:
60
+ val_node = para('')
61
+ else:
62
+ val_node = code(value)
63
+ return val_node
64
+
65
+ def format_single_value_file(value, package):
66
+ val_list = [code(value)]
67
+ if package:
68
+ val_list.append(nodes.inline(text=', '))
69
+ val_list.append(code(package))
70
+ return nodes.paragraph('', '', *val_list)
71
+
72
+ def format_value_file(is_list, value, package):
73
+ if is_list:
74
+ if len(value) > 1:
75
+ val_node = build_list([
76
+ format_single_value_file(v, p) for v, p in zip(value, package)])
77
+ elif len(value) > 0:
78
+ return format_single_value_file(value[0], package[0])
79
+ else:
80
+ val_node = para('')
81
+ else:
82
+ val_node = format_single_value_file(value, package)
83
+ return val_node
84
+
85
+ schema = Schema(rooted_cfg)
86
+ for kp in schema.allkeys():
87
+ if skip_zero_weight and \
88
+ len(kp) == 6 and kp[0] == 'flowgraph' and kp[-2] == 'weight' and \
89
+ schema.get(*kp) == 0:
90
+ continue
91
+
92
+ values = schema._getvals(*kp, return_defvalue=False)
93
+ if values:
94
+ # take first of multiple possible values
95
+ value, step, index = values[0]
96
+ val_type = schema.get(*kp, field='type')
97
+ is_filedir = 'file' in val_type or 'dir' in val_type
98
+ # Don't display false booleans
99
+ if val_type == 'bool' and value is False:
100
+ continue
101
+ if is_filedir:
102
+ val_node = format_value_file(val_type.startswith('['), value,
103
+ schema.get(*kp, field='package',
104
+ step=step, index=index))
105
+ else:
106
+ val_node = format_value(val_type.startswith('['), value)
107
+
108
+ # HTML builder fails if we don't make a text node the parent of the
109
+ # reference node returned by keypath()
110
+ p = nodes.paragraph()
111
+ p += keypath(kp, refdoc)
112
+ table.append([p, val_node])
113
+
114
+ if len(table) > 1:
115
+ # This colspec creates two columns of equal width that fill the entire
116
+ # page, and adds line breaks if table cell contents are longer than one
117
+ # line. "\X" is defined by Sphinx, otherwise this is standard LaTeX.
118
+ colspec = r'{|\X{1}{2}|\X{1}{2}|}'
119
+ return build_table(table, colspec=colspec)
120
+ else:
121
+ return None
122
+
123
+
124
+ def build_package_table(schema):
125
+ def collect_packages(cfg):
126
+ packages = []
127
+ if Schema._is_leaf(cfg):
128
+ if 'dir' in cfg['type'] or 'file' in cfg['type']:
129
+ for _, index_data in cfg['node'].items():
130
+ for _, data in index_data.items():
131
+ packages.extend(data['package'])
132
+ else:
133
+ for key in cfg:
134
+ packages.extend(collect_packages(cfg[key]))
135
+ packages = [p for p in packages if p]
136
+ return list(set(packages))
137
+
138
+ schema = Schema(cfg=schema)
139
+ packages = collect_packages(schema.cfg)
140
+
141
+ if not packages:
142
+ return None
143
+
144
+ # This colspec creates two columns of equal width that fill the entire
145
+ # page, and adds line breaks if table cell contents are longer than one
146
+ # line. "\X" is defined by Sphinx, otherwise this is standard LaTeX.
147
+ colspec = r'{|\X{1}{2}|\X{1}{2}|}'
148
+
149
+ table = [[strong('Package'), strong('Specifications')]]
150
+ for package in packages:
151
+ path = schema.get('package', 'source', package, 'path')
152
+ ref = schema.get('package', 'source', package, 'ref')
153
+
154
+ specs = [nodes.paragraph('', 'Path: ', code(path))]
155
+ if ref:
156
+ specs.append(nodes.paragraph('', 'Reference: ', code(ref)))
157
+
158
+ table.append([para(package), build_list(specs)])
159
+
160
+ return build_table(table, colspec=colspec)
161
+
162
+
163
+ #############
164
+ # Base class
165
+ #############
166
+
167
+
168
+ def flag_opt(argument):
169
+ if argument is not None:
170
+ raise ValueError('Flag should not have content')
171
+ return True
172
+
173
+
174
+ class DynamicGen(SphinxDirective):
175
+ '''Base class for all three directives provided by this extension.
176
+
177
+ Each child class implements a directive by overriding the display_config()
178
+ method and setting a PATH member variable.
179
+ '''
180
+
181
+ option_spec = {'nobuiltins': flag_opt}
182
+
183
+ def document_module(self, module, modname, path):
184
+ '''Build section documenting given module and name.'''
185
+ print(f'Generating docs for module {modname}...')
186
+
187
+ s = build_section_with_target(modname, self.get_document_ref_key(modname),
188
+ self.state.document)
189
+
190
+ # Attempt to use module doc string first
191
+ if not self.generate_documentation_from_object(module, path, s):
192
+ setup = self.get_setup_method(module)
193
+ # Then use setup doc string
194
+ self.generate_documentation_from_object(setup, path, s)
195
+
196
+ try:
197
+ chips = self.configure_chip_for_docs(module)
198
+ except Exception as e:
199
+ print("Failed:", e)
200
+ return None
201
+
202
+ if not chips:
203
+ return None
204
+
205
+ if not isinstance(chips, list):
206
+ chips = [chips]
207
+
208
+ for chip in chips:
209
+ extra_content = self.extra_content(chip, modname)
210
+ if extra_content is not None:
211
+ s += extra_content
212
+
213
+ package_info = self.package_information(chip, modname)
214
+ if package_info is not None:
215
+ s += [package_info]
216
+
217
+ disp = self.display_config(chip, modname)
218
+ if disp:
219
+ s += disp
220
+
221
+ child_content = self.child_content(path, module, modname)
222
+ if child_content is not None:
223
+ s += child_content
224
+
225
+ return s
226
+
227
+ def run(self):
228
+ '''Main entry point of directive.'''
229
+ sections = []
230
+ self.env.note_dependency(__file__)
231
+ self.env.note_dependency(utils.__file__)
232
+
233
+ for module, modname in self.get_modules():
234
+ path = module.__file__
235
+ self.env.note_dependency(path)
236
+ docs = self.document_module(module, modname, path)
237
+ if docs is not None:
238
+ sections.append((docs, modname))
239
+
240
+ if len(sections) > 0:
241
+ # Sort sections by module name
242
+ sections = sorted(sections, key=lambda t: t[1])
243
+ # Strip off modname so we just return list of docutils sections
244
+ sections, _ = zip(*sections)
245
+
246
+ return list(sections)
247
+
248
+ def get_modules(self):
249
+ '''Gets dynamic modules under `self.PATH`.
250
+
251
+ This function explicitly searches builtins.
252
+ '''
253
+ builtins_dir = f'{SC_ROOT}/siliconcompiler/{self.PATH}'
254
+ if 'nobuiltins' not in self.options:
255
+ modules = self.get_modules_in_dir(builtins_dir)
256
+ else:
257
+ modules = []
258
+
259
+ external_paths = os.getenv(self.SEARCH_ENV, "").split(':')
260
+ for scpath in external_paths:
261
+ if not scpath:
262
+ continue
263
+ if not os.path.isdir(scpath):
264
+ print(f'{scpath} not found')
265
+ raise FileNotFoundError(scpath)
266
+ if builtins_dir == scpath:
267
+ continue
268
+ modules.extend(self.get_modules_in_dir(scpath))
269
+
270
+ return modules
271
+
272
+ def get_modules_in_dir(self, module_dir):
273
+ '''Routine for getting modules and their names from a certain
274
+ directory.'''
275
+ modules = []
276
+ for importer, modname, _ in pkgutil.iter_modules([module_dir]):
277
+ module = importer.find_module(modname).load_module(modname)
278
+ modules.append((module, modname))
279
+
280
+ return modules
281
+
282
+ def parse_rst(self, content, s):
283
+ '''Helper for parsing reStructuredText content, adding it directly to
284
+ section `s`.'''
285
+ rst = ViewList()
286
+ # use fake filename 'inline' for error # reporting
287
+ for i, line in enumerate(content.split('\n')):
288
+ rst.append(line, 'inline', i)
289
+ nested_parse_with_titles(self.state, rst, s)
290
+
291
+ def package_information(self, chip, modname):
292
+ packages = build_package_table(chip.schema.cfg)
293
+ if packages:
294
+ sec = build_section('Data sources', self.get_data_source_ref_key(modname, chip.design))
295
+ sec += packages
296
+ return sec
297
+
298
+ return None
299
+
300
+ def extra_content(self, chip, modname):
301
+ '''Adds extra content to documentation.
302
+
303
+ May return a list of docutils nodes that will be added to the
304
+ documentation in between a module's docstring and configuration table.
305
+ Otherwise, if return value is None, don't add anything.
306
+ '''
307
+ return None
308
+
309
+ def child_content(self, path, module, modname):
310
+ return None
311
+
312
+ def generate_documentation_from_object(self, func, path, s):
313
+ # raw docstrings have funky indentation (basically, each line is already
314
+ # indented as much as the function), so we call trim() helper function
315
+ # to clean it up
316
+ docstr = utils.trim(func.__doc__)
317
+
318
+ if docstr:
319
+ self.parse_rst(docstr, s)
320
+ else:
321
+ return False
322
+
323
+ builtin = os.path.abspath(path).startswith(SC_ROOT)
324
+
325
+ if builtin:
326
+ relpath = path[len(SC_ROOT) + 1:]
327
+ gh_root = 'https://github.com/siliconcompiler/siliconcompiler/blob/main'
328
+ gh_link = f'{gh_root}/{relpath}'
329
+ filename = os.path.basename(relpath)
330
+ p = para('Setup file: ')
331
+ p += link(gh_link, text=filename)
332
+ s += p
333
+
334
+ return True
335
+
336
+ def _document_free_params(self, cfg, type, key_path, reference_prefix, s):
337
+ if type in cfg:
338
+ cfg = cfg[type]
339
+ else:
340
+ return
341
+
342
+ if type == "var":
343
+ type_heading = "Variables"
344
+ elif type == "file":
345
+ type_heading = "Files"
346
+
347
+ table = [[strong('Parameters'), strong('Help')]]
348
+ for key, params in cfg.items():
349
+ if key == "default":
350
+ continue
351
+
352
+ key_node = nodes.paragraph()
353
+ key_node += keypath(key_path + [key], self.env.docname,
354
+ key_text=["...", f"'{type}'", f"'{key}'"])
355
+ table.append([key_node, para(params["help"])])
356
+
357
+ if len(table) > 1:
358
+ s += build_section(type_heading, self.get_ref(*reference_prefix, type))
359
+ colspec = r'{|\X{1}{2}|\X{1}{2}|}'
360
+ s += build_table(table, colspec=colspec)
361
+
362
+ def get_make_docs_method(self, module):
363
+ return getattr(module, 'make_docs', None)
364
+
365
+ def get_configure_docs_method(self, module):
366
+ return getattr(module, 'configure_docs', None)
367
+
368
+ def get_setup_method(self, module):
369
+ return getattr(module, 'setup', None)
370
+
371
+ def make_chip(self):
372
+ return siliconcompiler.Chip('<design>')
373
+
374
+ def _handle_make_docs(self, chip, module):
375
+ make_docs = self.get_make_docs_method(module)
376
+ if make_docs:
377
+ new_chip = make_docs(chip)
378
+ if new_chip:
379
+ # make_docs returned something so it's fully configured
380
+ return (new_chip, True)
381
+ else:
382
+ return (chip, False)
383
+ return (None, False)
384
+
385
+ def _handle_setup(self, chip, module):
386
+ setup = self.get_setup_method(module)
387
+ if not setup:
388
+ return None
389
+ new_chip = setup(chip)
390
+ if new_chip:
391
+ return new_chip
392
+ else:
393
+ # Setup didn't return anything so return the Chip object
394
+ return chip
395
+
396
+ def configure_chip_for_docs(self, module):
397
+ chip = self.make_chip()
398
+ docs_chip, docs_configured = self._handle_make_docs(chip, module)
399
+ if docs_chip and docs_configured:
400
+ return docs_chip
401
+
402
+ return self._handle_setup(chip, module)
403
+
404
+ def get_ref_prefix(self):
405
+ return self.REF_PREFIX
406
+
407
+ @staticmethod
408
+ def get_ref_key(*path):
409
+ return '-'.join(path)
410
+
411
+ def get_ref(self, *sections):
412
+ return DynamicGen.get_ref_key(self.get_ref_prefix(), *sections)
413
+
414
+ def get_configuration_ref_key(self, *modname):
415
+ return self.get_ref(*modname, 'configuration')
416
+
417
+ def get_data_source_ref_key(self, *modname):
418
+ return self.get_ref(*modname, 'data_source')
419
+
420
+ def get_document_ref_key(self, *modname):
421
+ return self.get_ref(*modname)
422
+
423
+ def build_config_recursive(self, schema, refdoc, keypath=None, sec_key_prefix=None):
424
+ '''Helper function for displaying schema at each level as tables under nested
425
+ sections.
426
+
427
+ For each item:
428
+ - If it's a leaf, collect it into a table we will display at this
429
+ level
430
+ - Otherwise, recurse and collect sections of lower levels
431
+ '''
432
+ if keypath is None:
433
+ keypath = []
434
+ if sec_key_prefix is None:
435
+ sec_key_prefix = []
436
+
437
+ leaves = {}
438
+ child_sections = []
439
+ for key in schema.getkeys(*keypath):
440
+ if Schema._is_leaf(schema.getdict(*keypath, key)):
441
+ val = schema.getdict(*keypath, key)
442
+ leaves.update({key: val})
443
+ else:
444
+ children = self.build_config_recursive(
445
+ schema,
446
+ refdoc,
447
+ keypath=keypath + [key],
448
+ sec_key_prefix=sec_key_prefix)
449
+ child_sections.extend(children)
450
+
451
+ schema_table = None
452
+ if len(leaves) > 0:
453
+ # Might return None is none of the leaves are displayable
454
+ schema_table = build_schema_value_table(leaves, refdoc, keypath_prefix=keypath)
455
+
456
+ if schema_table is not None:
457
+ # If we've found leaves, create a new section where we'll display a
458
+ # table plus all child sections.
459
+ keypathstr = ', '.join(keypath)
460
+ top = build_section(keypathstr, self.get_ref(*sec_key_prefix, 'key', *keypath))
461
+ top += schema_table
462
+ top += child_sections
463
+ return [top]
464
+
465
+ # Otherwise, just pass on the child sections -- we don't want to
466
+ # create an extra level of section hierarchy for levels of the
467
+ # schema without leaves.
468
+ return child_sections
469
+
470
+ #########################
471
+ # Specialized extensions
472
+ #########################
473
+
474
+
475
+ class FlowGen(DynamicGen):
476
+ PATH = 'flows'
477
+ REF_PREFIX = 'flows'
478
+ SEARCH_ENV = "SC_DOCS_FLOWS"
479
+
480
+ def extra_content(self, chip, modname):
481
+ flow_path = os.path.join(self.env.app.outdir, f'_images/gen/{modname}.svg')
482
+ chip.write_flowgraph(flow_path, flow=chip.design)
483
+ return [image(flow_path, center=True)]
484
+
485
+ def display_config(self, chip, modname):
486
+ '''Display parameters under `flowgraph, <step>`, `metric, <step>` and
487
+ `showtool`. Parameters are grouped into sections by step, with an
488
+ additional table for non-step items.
489
+ '''
490
+
491
+ name = chip.design
492
+
493
+ settings = build_section('Configuration', self.get_configuration_ref_key(name))
494
+
495
+ steps = chip.getkeys('flowgraph', chip.design)
496
+ # TODO: should try to order?
497
+
498
+ # Build section + table for each step (combining entries under flowgraph
499
+ # and metric)
500
+ for step in steps:
501
+ section = build_section(step, self.get_ref(name, 'step', step))
502
+ step_cfg = {}
503
+ cfg = chip.getdict('flowgraph', chip.design, step)
504
+ if cfg is None:
505
+ continue
506
+ schema = Schema(cfg=cfg)
507
+ schema.prune()
508
+ pruned = schema.cfg
509
+ if chip.design not in step_cfg:
510
+ step_cfg[chip.design] = {}
511
+ step_cfg[chip.design][step] = pruned
512
+
513
+ section += build_schema_value_table(step_cfg,
514
+ self.env.docname,
515
+ keypath_prefix=['flowgraph'],
516
+ skip_zero_weight=True)
517
+ settings += section
518
+
519
+ return settings
520
+
521
+
522
+ class PDKGen(DynamicGen):
523
+ PATH = 'pdks'
524
+ REF_PREFIX = 'pdks'
525
+ SEARCH_ENV = "SC_DOCS_PDKS"
526
+
527
+ def display_config(self, chip, modname):
528
+ '''Display parameters under `pdk`, `asic`, and `library` in nested form.'''
529
+
530
+ name = chip.design
531
+
532
+ settings = build_section('Configuration', self.get_configuration_ref_key(name))
533
+
534
+ config_settings = self.build_config_recursive(
535
+ chip.schema,
536
+ self.env.docname,
537
+ keypath=['pdk'],
538
+ sec_key_prefix=[name])
539
+
540
+ if config_settings:
541
+ settings += config_settings
542
+ return settings
543
+
544
+ return None
545
+
546
+
547
+ class LibGen(DynamicGen):
548
+ PATH = 'libs'
549
+ REF_PREFIX = 'libs'
550
+ SEARCH_ENV = "SC_DOCS_LIBS"
551
+
552
+ def extra_content(self, chip, modname):
553
+ # assume same pdk for all libraries configured by this module
554
+ pdk = chip.get('option', 'pdk')
555
+
556
+ p = docutils.nodes.inline('')
557
+ pdk_ref = "None"
558
+ if pdk:
559
+ pdkid = get_ref_id(DynamicGen.get_ref_key(PDKGen.REF_PREFIX, pdk))
560
+ pdk_ref = f":ref:`{pdk}<{pdkid}>`"
561
+ self.parse_rst(f'Associated PDK: {pdk_ref}', p)
562
+
563
+ return [p]
564
+
565
+ def display_config(self, chip, modname):
566
+ '''Display parameters under in nested form.'''
567
+
568
+ sections = []
569
+
570
+ libname = chip.design
571
+
572
+ settings = build_section_with_target(libname, self.get_ref(libname),
573
+ self.state.document)
574
+
575
+ for key in ('asic', 'input', 'output', 'option'):
576
+ settings += self.build_config_recursive(
577
+ chip.schema,
578
+ self.env.docname,
579
+ keypath=[key],
580
+ sec_key_prefix=[libname, key])
581
+
582
+ sections.append(settings)
583
+
584
+ return sections
585
+
586
+ def get_document_ref_key(self, *modname):
587
+ return super().get_document_ref_key(*modname, "grouping")
588
+
589
+
590
+ class ToolGen(DynamicGen):
591
+ PATH = 'tools'
592
+ REF_PREFIX = 'tools'
593
+ SEARCH_ENV = "SC_DOCS_TOOLS"
594
+
595
+ def make_chip(self):
596
+ chip = super().make_chip()
597
+ self._setup_chip(chip, '<tool>', '<task>')
598
+
599
+ return chip
600
+
601
+ def _setup_chip(self, chip, tool_name, task_name):
602
+ step = chip.get('arg', 'step')
603
+ if not step:
604
+ step = '<step>'
605
+ index = chip.get('arg', 'index')
606
+ if not index:
607
+ index = '<index>'
608
+ chip.set('arg', 'step', step)
609
+ chip.set('arg', 'index', index)
610
+
611
+ flow = chip.get('option', 'flow')
612
+ if not flow:
613
+ flow = '<flow>'
614
+ chip.set('option', 'flow', flow)
615
+ chip.set('flowgraph', flow, step, index, 'tool', tool_name)
616
+ chip.set('flowgraph', flow, step, index, 'task', task_name)
617
+
618
+ def configure_chip_for_docs(self, module, toolmodule=None):
619
+ chip = self.make_chip()
620
+ docs_chip, docs_configured = self._handle_make_docs(chip, module)
621
+ if not docs_chip and toolmodule:
622
+ docs_chip, docs_configured = self._handle_make_docs(chip, toolmodule)
623
+ if docs_configured:
624
+ return docs_chip
625
+
626
+ # set values for current step
627
+ toolname = module.__name__
628
+ if self.__tool:
629
+ toolname = self.__tool
630
+ taskname = '<task>'
631
+ if self.__task:
632
+ taskname = self.__task
633
+ self._setup_chip(chip, toolname, taskname)
634
+
635
+ if toolmodule:
636
+ return chip
637
+ else:
638
+ return self._handle_setup(chip, module)
639
+
640
+ def display_config(self, chip, modname):
641
+ '''Display config under `eda, <modname>` in a single table.'''
642
+ cfg = chip.getdict('tool', modname)
643
+ schema = Schema(cfg=cfg)
644
+ schema.prune()
645
+ pruned = schema.cfg
646
+ if 'task' in pruned:
647
+ # Remove task specific items since they will be documented
648
+ # by the task documentation
649
+ del pruned['task']
650
+ table = build_schema_value_table(pruned, self.env.docname, keypath_prefix=['tool', modname])
651
+ if table is not None:
652
+ return table
653
+ else:
654
+ return []
655
+
656
+ def task_display_config(self, chip, toolname, taskname):
657
+ '''Display config under `eda, <modname>` in a single table.'''
658
+ cfg = chip.getdict('tool', toolname, 'task', taskname)
659
+ schema = Schema(cfg=cfg)
660
+ schema.prune()
661
+ pruned = schema.cfg
662
+ table = build_schema_value_table(pruned, self.env.docname,
663
+ keypath_prefix=['tool', toolname, 'task', taskname])
664
+ if table is not None:
665
+ return table
666
+ else:
667
+ return []
668
+
669
+ def get_modules_in_dir(self, module_dir):
670
+ self.__tool = None
671
+ self.__task = None
672
+ '''Custom implementation for ToolGen since the tool setup modules are
673
+ under an extra directory, and this way we don't have to force users to
674
+ add an __init__.py to make the directory a module itself.
675
+ '''
676
+ modules = []
677
+ for toolname in os.listdir(module_dir):
678
+ if (toolname == "template" or toolname.startswith('_')):
679
+ # No need to include empty template in documentation
680
+ continue
681
+ # skip over directories/files that don't match the structure of tool
682
+ # directories (otherwise we'll get confused by Python metadata like
683
+ # __pycache__/)
684
+ if not os.path.isdir(f'{module_dir}/{toolname}'):
685
+ continue
686
+ path = f'{module_dir}/{toolname}/{toolname}.py'
687
+ if not os.path.exists(path):
688
+ path = f'{module_dir}/{toolname}/__init__.py'
689
+ if not os.path.exists(path):
690
+ continue
691
+
692
+ spec = importlib.util.spec_from_file_location(toolname, path)
693
+ module = importlib.util.module_from_spec(spec)
694
+ spec.loader.exec_module(module)
695
+
696
+ modules.append((module, toolname))
697
+
698
+ return modules
699
+
700
+ def document_task(self, path, toolmodule, taskmodule, taskname, toolname):
701
+ self.env.note_dependency(path)
702
+ s = build_section_with_target(taskname, self.get_ref(toolname, taskname),
703
+ self.state.document)
704
+
705
+ # Find setup function
706
+ task_setup = self.get_setup_method(taskmodule)
707
+ if not task_setup:
708
+ return None
709
+
710
+ print(f"Generating docs for task {toolname}/{taskname}...")
711
+
712
+ self.generate_documentation_from_object(task_setup, path, s)
713
+
714
+ self.__tool = toolname
715
+ self.__task = taskname
716
+ chip = self.configure_chip_for_docs(taskmodule, toolmodule=toolmodule)
717
+ self.__tool = None
718
+ self.__task = None
719
+
720
+ # Annotate the target used for default values
721
+ if chip.valid('option', 'target') and chip.get('option', 'target'):
722
+ p = docutils.nodes.inline('')
723
+ target = chip.get('option', 'target').split('.')[-1]
724
+ targetid = get_ref_id(DynamicGen.get_ref_key(TargetGen.REF_PREFIX, target))
725
+ self.parse_rst(f"Built using target: :ref:`{target}<{targetid}>`", p)
726
+ s += p
727
+
728
+ try:
729
+ task_setup(chip)
730
+
731
+ config = build_section("Configuration", self.get_configuration_ref_key(toolname,
732
+ taskname))
733
+ config_table = self.task_display_config(chip, toolname, taskname)
734
+ if config_table:
735
+ s += config
736
+ s += config_table
737
+ self.document_free_params(chip.getdict('tool', toolname, 'task', taskname),
738
+ [toolname, taskname, 'params'],
739
+ s)
740
+ except Exception as e:
741
+ print('Failed to document task, Chip object probably not configured correctly.')
742
+ print(e)
743
+ return None
744
+
745
+ return s
746
+
747
+ def child_content(self, path, module, modname):
748
+ sections = []
749
+ path = os.path.abspath(path)
750
+ module_dir = os.path.dirname(path)
751
+ for taskfile in os.listdir(module_dir):
752
+ if taskfile == "__init__.py":
753
+ # skip init
754
+ continue
755
+
756
+ task_path = os.path.join(module_dir, taskfile)
757
+ if path == task_path:
758
+ # skip tool module
759
+ continue
760
+
761
+ if not os.path.isfile(task_path):
762
+ # skip if not a file
763
+ continue
764
+
765
+ spec = importlib.util.spec_from_file_location(taskfile, task_path)
766
+ if not spec:
767
+ # unable to load, probably not a python file
768
+ continue
769
+ taskmodule = importlib.util.module_from_spec(spec)
770
+ try:
771
+ spec.loader.exec_module(taskmodule)
772
+ except Exception:
773
+ # Module failed to load
774
+ # klayout imports pya which is only defined in klayout
775
+ continue
776
+
777
+ taskname = os.path.splitext(os.path.basename(task_path))[0]
778
+
779
+ task_doc = self.document_task(task_path, module, taskmodule, taskname, modname)
780
+ if task_doc:
781
+ sections.append((taskname, task_doc))
782
+
783
+ if len(sections) > 0:
784
+ sections = sorted(sections, key=lambda t: t[0])
785
+ # Strip off modname so we just return list of docutils sections
786
+ _, sections = zip(*sections)
787
+ return sections
788
+
789
+ def document_free_params(self, cfg, reference_prefix, s):
790
+ key_path = ['tool', '<tool>', 'task', '<task>']
791
+ self._document_free_params(cfg, 'var', key_path + ['var'], reference_prefix, s)
792
+ self._document_free_params(cfg, 'file', key_path + ['file'], reference_prefix, s)
793
+
794
+
795
+ class TargetGen(DynamicGen):
796
+ PATH = 'targets'
797
+ REF_PREFIX = 'targets'
798
+ SEARCH_ENV = "SC_DOCS_TARGETS"
799
+
800
+ def build_module_list(self, chip, header, modtype, targetname, *refprefix):
801
+ modules = chip._loaded_modules[modtype]
802
+ if len(modules) > 0:
803
+ section = build_section(header, self.get_ref(targetname, modtype))
804
+ modlist = nodes.bullet_list()
805
+ for module in modules:
806
+ list_item = nodes.list_item()
807
+ # TODO: replace with proper docutils nodes: sphinx.addnodes.pending_xref
808
+ modkey = get_ref_id(DynamicGen.get_ref_key(*refprefix, module))
809
+ self.parse_rst(f':ref:`{module}<{modkey}>`', list_item)
810
+ modlist += list_item
811
+
812
+ section += modlist
813
+ return section
814
+ return None
815
+
816
+ def display_config(self, chip, modname):
817
+ sections = []
818
+
819
+ flow_section = self.build_module_list(chip, 'Flows', 'flows', modname, FlowGen.REF_PREFIX)
820
+ if flow_section is not None:
821
+ sections.append(flow_section)
822
+
823
+ pdk_section = self.build_module_list(chip, 'PDK', 'pdks', modname, PDKGen.REF_PREFIX)
824
+ if pdk_section is not None:
825
+ sections.append(pdk_section)
826
+
827
+ libs_section = self.build_module_list(chip, 'Libraries', 'libs', modname, LibGen.REF_PREFIX)
828
+ if libs_section is not None:
829
+ sections.append(libs_section)
830
+
831
+ checklist_section = self.build_module_list(chip, 'Checklists', 'checklists', modname,
832
+ ChecklistGen.REF_PREFIX)
833
+ if checklist_section is not None:
834
+ sections.append(checklist_section)
835
+
836
+ filtered_cfg = {}
837
+ for key in ('asic', 'constraint', 'option'):
838
+ filtered_cfg[key] = chip.getdict(key)
839
+ schema = Schema(cfg=filtered_cfg)
840
+ schema.prune()
841
+ pruned_cfg = schema.cfg
842
+
843
+ if len(pruned_cfg) > 0:
844
+ schema_section = build_section('Configuration', self.get_configuration_ref_key(modname))
845
+ schema_section += build_schema_value_table(pruned_cfg, self.env.docname)
846
+ sections.append(schema_section)
847
+
848
+ return sections
849
+
850
+
851
+ class AppGen(DynamicGen):
852
+ PATH = 'apps'
853
+ REF_PREFIX = 'apps'
854
+ SEARCH_ENV = "SC_DOCS_APPS"
855
+
856
+ def document_module(self, module, modname, path):
857
+ if modname[0] == "_":
858
+ return None
859
+
860
+ cmd_name = modname.replace('_', '-')
861
+ cmd = [cmd_name, '--help']
862
+
863
+ output = subprocess.check_output(cmd).decode('utf-8')
864
+
865
+ section = build_section(cmd_name, self.get_ref(cmd_name))
866
+ section += literalblock(output)
867
+
868
+ return section
869
+
870
+
871
+ class ChecklistGen(DynamicGen):
872
+ PATH = 'checklists'
873
+ REF_PREFIX = 'checklists'
874
+ SEARCH_ENV = "SC_DOCS_CHECKLISTS"
875
+
876
+ def display_config(self, chip, modname):
877
+ '''Display parameters under in nested form.'''
878
+
879
+ sections = []
880
+
881
+ name = chip.design
882
+
883
+ keypath_prefix = ['checklist', name]
884
+ cfg = chip.getdict(*keypath_prefix)
885
+
886
+ settings = build_section('Configuration', self.get_configuration_ref_key(name))
887
+
888
+ for key in cfg.keys():
889
+ if key == 'default':
890
+ continue
891
+ settings += build_section(key, self.get_ref(name, 'key', key))
892
+ settings += build_schema_value_table(cfg[key], self.env.docname,
893
+ keypath_prefix=[*keypath_prefix, key])
894
+
895
+ sections.append(settings)
896
+
897
+ return sections
898
+
899
+
900
+ class ExampleGen(DynamicGen):
901
+
902
+ def get_modules(self):
903
+ examples_dir = f'{SC_ROOT}/examples'
904
+
905
+ modules = []
906
+ for example in os.listdir(examples_dir):
907
+ if not os.path.isdir(f'{examples_dir}/{example}'):
908
+ continue
909
+ path = f'{examples_dir}/{example}/{example}.py'
910
+ if not os.path.exists(path):
911
+ continue
912
+
913
+ spec = importlib.util.spec_from_file_location(example, path)
914
+ module = importlib.util.module_from_spec(spec)
915
+ spec.loader.exec_module(module)
916
+
917
+ modules.append((module, example))
918
+
919
+ return modules
920
+
921
+ def document_module(self, module, modname, path):
922
+ section = build_section(modname, modname)
923
+
924
+ if not hasattr(module, 'main'):
925
+ return None
926
+
927
+ main = getattr(module, 'main')
928
+
929
+ # raw docstrings have funky indentation (basically, each line is already
930
+ # indented as much as the function), so we call trim() helper function
931
+ # to clean it up
932
+ docstr = utils.trim(main.__doc__)
933
+
934
+ if docstr:
935
+ self.parse_rst(docstr, section)
936
+
937
+ return section
938
+
939
+
940
+ def keypath_role(name, rawtext, text, lineno, inliner, options=None, content=None):
941
+ doc = inliner.document
942
+ env = doc.settings.env
943
+
944
+ # Split and clean up keypath
945
+ keys = [key.strip() for key in text.split(',')]
946
+ try:
947
+ return [keypath(keys, env.docname)], []
948
+ except ValueError as e:
949
+ msg = inliner.reporter.error(f'{rawtext}: {e}', line=lineno)
950
+ prb = inliner.problematic(rawtext, rawtext, msg)
951
+ return [prb], [msg]
952
+
953
+
954
+ class SCDomain(StandardDomain):
955
+ name = 'sc'
956
+
957
+ # Override in StandardDomain so xref is literal instead of inline
958
+ # https://github.com/sphinx-doc/sphinx/blob/ba080286b06cb9e0cadec59a6cf1f96aa11aef5a/sphinx/domains/std.py#L789
959
+ def build_reference_node(self,
960
+ fromdocname,
961
+ builder,
962
+ docname,
963
+ labelid,
964
+ sectname,
965
+ rolename,
966
+ **options):
967
+ nodeclass = options.pop('nodeclass', nodes.reference)
968
+ newnode = nodeclass('', '', internal=True, **options)
969
+ innernode = nodes.literal(sectname, sectname)
970
+ if innernode.get('classes') is not None:
971
+ innernode['classes'].append('std')
972
+ innernode['classes'].append('std-' + rolename)
973
+ if docname == fromdocname:
974
+ newnode['refid'] = labelid
975
+ else:
976
+ # set more info in contnode; in case the
977
+ # get_relative_uri call raises NoUri,
978
+ # the builder will then have to resolve these
979
+ contnode = pending_xref('')
980
+ contnode['refdocname'] = docname
981
+ contnode['refsectname'] = sectname
982
+ newnode['refuri'] = builder.get_relative_uri(
983
+ fromdocname, docname)
984
+ if labelid:
985
+ newnode['refuri'] += '#' + labelid
986
+ newnode.append(innernode)
987
+ return newnode
988
+
989
+
990
+ def setup(app):
991
+ app.add_domain(SCDomain)
992
+ app.add_directive('flowgen', FlowGen)
993
+ app.add_directive('pdkgen', PDKGen)
994
+ app.add_directive('libgen', LibGen)
995
+ app.add_directive('toolgen', ToolGen)
996
+ app.add_directive('appgen', AppGen)
997
+ app.add_directive('examplegen', ExampleGen)
998
+ app.add_directive('targetgen', TargetGen)
999
+ app.add_directive('checklistgen', ChecklistGen)
1000
+ app.add_role('keypath', keypath_role)
1001
+
1002
+ return {
1003
+ 'version': siliconcompiler.__version__,
1004
+ 'parallel_read_safe': True,
1005
+ 'parallel_write_safe': True,
1006
+ }