siliconcompiler 0.28.9__py3-none-any.whl → 0.29.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 (164) hide show
  1. siliconcompiler/_metadata.py +1 -1
  2. siliconcompiler/apps/__init__.py +26 -0
  3. siliconcompiler/apps/sc_remote.py +15 -14
  4. siliconcompiler/apps/sc_show.py +5 -5
  5. siliconcompiler/apps/utils/replay.py +194 -0
  6. siliconcompiler/checklists/__init__.py +12 -0
  7. siliconcompiler/core.py +89 -22
  8. siliconcompiler/flows/__init__.py +34 -0
  9. siliconcompiler/flows/_common.py +11 -13
  10. siliconcompiler/flows/asicflow.py +83 -42
  11. siliconcompiler/flows/showflow.py +1 -1
  12. siliconcompiler/libs/__init__.py +5 -0
  13. siliconcompiler/optimizer/__init__.py +199 -0
  14. siliconcompiler/optimizer/vizier.py +259 -0
  15. siliconcompiler/pdks/__init__.py +5 -0
  16. siliconcompiler/remote/__init__.py +11 -0
  17. siliconcompiler/remote/client.py +753 -815
  18. siliconcompiler/report/report.py +2 -0
  19. siliconcompiler/report/summary_table.py +1 -1
  20. siliconcompiler/scheduler/__init__.py +118 -58
  21. siliconcompiler/scheduler/send_messages.py +1 -1
  22. siliconcompiler/schema/schema_cfg.py +16 -4
  23. siliconcompiler/schema/schema_obj.py +29 -10
  24. siliconcompiler/schema/utils.py +2 -0
  25. siliconcompiler/sphinx_ext/__init__.py +85 -0
  26. siliconcompiler/sphinx_ext/dynamicgen.py +19 -34
  27. siliconcompiler/sphinx_ext/schemagen.py +3 -2
  28. siliconcompiler/targets/__init__.py +26 -0
  29. siliconcompiler/targets/gf180_demo.py +3 -3
  30. siliconcompiler/templates/replay/replay.py.j2 +62 -0
  31. siliconcompiler/templates/replay/requirements.txt +7 -0
  32. siliconcompiler/templates/replay/setup.sh +130 -0
  33. siliconcompiler/tools/__init__.py +60 -0
  34. siliconcompiler/tools/_common/__init__.py +15 -1
  35. siliconcompiler/tools/_common/asic.py +17 -9
  36. siliconcompiler/tools/builtin/concatenate.py +1 -1
  37. siliconcompiler/tools/ghdl/ghdl.py +1 -2
  38. siliconcompiler/tools/klayout/convert_drc_db.py +1 -1
  39. siliconcompiler/tools/klayout/drc.py +1 -1
  40. siliconcompiler/tools/klayout/export.py +8 -1
  41. siliconcompiler/tools/klayout/klayout.py +2 -2
  42. siliconcompiler/tools/klayout/klayout_convert_drc_db.py +2 -2
  43. siliconcompiler/tools/klayout/klayout_export.py +7 -5
  44. siliconcompiler/tools/klayout/klayout_operations.py +4 -3
  45. siliconcompiler/tools/klayout/klayout_show.py +3 -2
  46. siliconcompiler/tools/klayout/klayout_utils.py +1 -1
  47. siliconcompiler/tools/klayout/operations.py +8 -0
  48. siliconcompiler/tools/klayout/screenshot.py +6 -1
  49. siliconcompiler/tools/klayout/show.py +8 -1
  50. siliconcompiler/tools/magic/magic.py +1 -1
  51. siliconcompiler/tools/openroad/__init__.py +103 -0
  52. siliconcompiler/tools/openroad/{openroad.py → _apr.py} +415 -423
  53. siliconcompiler/tools/openroad/antenna_repair.py +78 -0
  54. siliconcompiler/tools/openroad/clock_tree_synthesis.py +64 -0
  55. siliconcompiler/tools/openroad/detailed_placement.py +59 -0
  56. siliconcompiler/tools/openroad/detailed_route.py +62 -0
  57. siliconcompiler/tools/openroad/endcap_tapcell_insertion.py +52 -0
  58. siliconcompiler/tools/openroad/fillercell_insertion.py +58 -0
  59. siliconcompiler/tools/openroad/{dfm.py → fillmetal_insertion.py} +35 -19
  60. siliconcompiler/tools/openroad/global_placement.py +58 -0
  61. siliconcompiler/tools/openroad/global_route.py +63 -0
  62. siliconcompiler/tools/openroad/init_floorplan.py +103 -0
  63. siliconcompiler/tools/openroad/macro_placement.py +65 -0
  64. siliconcompiler/tools/openroad/metrics.py +23 -8
  65. siliconcompiler/tools/openroad/pin_placement.py +56 -0
  66. siliconcompiler/tools/openroad/power_grid.py +65 -0
  67. siliconcompiler/tools/openroad/rcx_bench.py +7 -4
  68. siliconcompiler/tools/openroad/rcx_extract.py +2 -1
  69. siliconcompiler/tools/openroad/rdlroute.py +4 -4
  70. siliconcompiler/tools/openroad/repair_design.py +59 -0
  71. siliconcompiler/tools/openroad/repair_timing.py +63 -0
  72. siliconcompiler/tools/openroad/screenshot.py +9 -20
  73. siliconcompiler/tools/openroad/scripts/apr/postamble.tcl +44 -0
  74. siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +95 -0
  75. siliconcompiler/tools/openroad/scripts/apr/sc_antenna_repair.tcl +51 -0
  76. siliconcompiler/tools/openroad/scripts/apr/sc_clock_tree_synthesis.tcl +66 -0
  77. siliconcompiler/tools/openroad/scripts/apr/sc_detailed_placement.tcl +41 -0
  78. siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +71 -0
  79. siliconcompiler/tools/openroad/scripts/apr/sc_endcap_tapcell_insertion.tcl +55 -0
  80. siliconcompiler/tools/openroad/scripts/apr/sc_fillercell_insertion.tcl +27 -0
  81. siliconcompiler/tools/openroad/scripts/apr/sc_fillmetal_insertion.tcl +36 -0
  82. siliconcompiler/tools/openroad/scripts/apr/sc_global_placement.tcl +26 -0
  83. siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +61 -0
  84. siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +333 -0
  85. siliconcompiler/tools/openroad/scripts/apr/sc_macro_placement.tcl +123 -0
  86. siliconcompiler/tools/openroad/scripts/apr/sc_metrics.tcl +22 -0
  87. siliconcompiler/tools/openroad/scripts/apr/sc_pin_placement.tcl +41 -0
  88. siliconcompiler/tools/openroad/scripts/apr/sc_power_grid.tcl +60 -0
  89. siliconcompiler/tools/openroad/scripts/apr/sc_repair_design.tcl +68 -0
  90. siliconcompiler/tools/openroad/scripts/apr/sc_repair_timing.tcl +83 -0
  91. siliconcompiler/tools/openroad/scripts/apr/sc_write_data.tcl +125 -0
  92. siliconcompiler/tools/openroad/scripts/common/debugging.tcl +28 -0
  93. siliconcompiler/tools/openroad/scripts/common/procs.tcl +727 -0
  94. siliconcompiler/tools/openroad/scripts/common/read_input_files.tcl +59 -0
  95. siliconcompiler/tools/openroad/scripts/common/read_liberty.tcl +20 -0
  96. siliconcompiler/tools/openroad/scripts/common/read_timing_constraints.tcl +16 -0
  97. siliconcompiler/tools/openroad/scripts/common/reports.tcl +180 -0
  98. siliconcompiler/tools/openroad/scripts/common/screenshot.tcl +18 -0
  99. siliconcompiler/tools/openroad/scripts/common/write_images.tcl +395 -0
  100. siliconcompiler/tools/openroad/scripts/{sc_rcx_bench.tcl → rcx/sc_rcx_bench.tcl} +5 -5
  101. siliconcompiler/tools/openroad/scripts/{sc_rcx_extract.tcl → rcx/sc_rcx_extract.tcl} +0 -0
  102. siliconcompiler/tools/openroad/scripts/sc_rcx.tcl +5 -16
  103. siliconcompiler/tools/openroad/scripts/sc_rdlroute.tcl +51 -51
  104. siliconcompiler/tools/openroad/scripts/sc_show.tcl +110 -0
  105. siliconcompiler/tools/openroad/show.py +28 -23
  106. siliconcompiler/tools/openroad/{export.py → write_data.py} +31 -26
  107. siliconcompiler/tools/opensta/__init__.py +2 -2
  108. siliconcompiler/tools/opensta/check_library.py +27 -0
  109. siliconcompiler/tools/opensta/scripts/sc_check_library.tcl +255 -0
  110. siliconcompiler/tools/opensta/scripts/sc_timing.tcl +1 -1
  111. siliconcompiler/tools/sv2v/sv2v.py +1 -2
  112. siliconcompiler/tools/verilator/verilator.py +6 -7
  113. siliconcompiler/tools/vivado/vivado.py +1 -1
  114. siliconcompiler/tools/yosys/__init__.py +149 -0
  115. siliconcompiler/tools/yosys/lec.py +22 -9
  116. siliconcompiler/tools/yosys/sc_lec.tcl +94 -49
  117. siliconcompiler/tools/yosys/sc_syn.tcl +1 -0
  118. siliconcompiler/tools/yosys/screenshot.py +2 -2
  119. siliconcompiler/tools/yosys/syn_asic.py +105 -74
  120. siliconcompiler/tools/yosys/syn_asic.tcl +58 -12
  121. siliconcompiler/tools/yosys/syn_fpga.py +2 -3
  122. siliconcompiler/tools/yosys/syn_fpga.tcl +26 -19
  123. siliconcompiler/toolscripts/_tools.json +5 -5
  124. siliconcompiler/utils/__init__.py +7 -3
  125. {siliconcompiler-0.28.9.dist-info → siliconcompiler-0.29.1.dist-info}/METADATA +22 -17
  126. {siliconcompiler-0.28.9.dist-info → siliconcompiler-0.29.1.dist-info}/RECORD +131 -114
  127. {siliconcompiler-0.28.9.dist-info → siliconcompiler-0.29.1.dist-info}/WHEEL +1 -1
  128. {siliconcompiler-0.28.9.dist-info → siliconcompiler-0.29.1.dist-info}/entry_points.txt +13 -0
  129. siliconcompiler/libs/asap7sc7p5t.py +0 -8
  130. siliconcompiler/libs/gf180mcu.py +0 -8
  131. siliconcompiler/libs/interposer.py +0 -8
  132. siliconcompiler/libs/nangate45.py +0 -8
  133. siliconcompiler/libs/sg13g2_stdcell.py +0 -8
  134. siliconcompiler/libs/sky130hd.py +0 -8
  135. siliconcompiler/libs/sky130io.py +0 -8
  136. siliconcompiler/pdks/asap7.py +0 -8
  137. siliconcompiler/pdks/freepdk45.py +0 -8
  138. siliconcompiler/pdks/gf180.py +0 -8
  139. siliconcompiler/pdks/ihp130.py +0 -8
  140. siliconcompiler/pdks/interposer.py +0 -8
  141. siliconcompiler/pdks/skywater130.py +0 -8
  142. siliconcompiler/tools/openroad/cts.py +0 -45
  143. siliconcompiler/tools/openroad/floorplan.py +0 -75
  144. siliconcompiler/tools/openroad/physyn.py +0 -27
  145. siliconcompiler/tools/openroad/place.py +0 -41
  146. siliconcompiler/tools/openroad/route.py +0 -45
  147. siliconcompiler/tools/openroad/scripts/__init__.py +0 -0
  148. siliconcompiler/tools/openroad/scripts/sc_apr.tcl +0 -514
  149. siliconcompiler/tools/openroad/scripts/sc_cts.tcl +0 -68
  150. siliconcompiler/tools/openroad/scripts/sc_dfm.tcl +0 -22
  151. siliconcompiler/tools/openroad/scripts/sc_export.tcl +0 -100
  152. siliconcompiler/tools/openroad/scripts/sc_floorplan.tcl +0 -456
  153. siliconcompiler/tools/openroad/scripts/sc_metrics.tcl +0 -1
  154. siliconcompiler/tools/openroad/scripts/sc_physyn.tcl +0 -6
  155. siliconcompiler/tools/openroad/scripts/sc_place.tcl +0 -84
  156. siliconcompiler/tools/openroad/scripts/sc_procs.tcl +0 -494
  157. siliconcompiler/tools/openroad/scripts/sc_report.tcl +0 -189
  158. siliconcompiler/tools/openroad/scripts/sc_route.tcl +0 -143
  159. siliconcompiler/tools/openroad/scripts/sc_screenshot.tcl +0 -18
  160. siliconcompiler/tools/openroad/scripts/sc_write_images.tcl +0 -393
  161. siliconcompiler/tools/yosys/yosys.py +0 -148
  162. /siliconcompiler/tools/openroad/scripts/{sc_write.tcl → common/write_data.tcl} +0 -0
  163. {siliconcompiler-0.28.9.dist-info → siliconcompiler-0.29.1.dist-info}/LICENSE +0 -0
  164. {siliconcompiler-0.28.9.dist-info → siliconcompiler-0.29.1.dist-info}/top_level.txt +0 -0
@@ -4,13 +4,22 @@ from siliconcompiler.flows._common import setup_multiple_frontends
4
4
  from siliconcompiler.flows._common import _make_docs
5
5
 
6
6
  from siliconcompiler.tools.yosys import syn_asic
7
- from siliconcompiler.tools.openroad import floorplan
8
- from siliconcompiler.tools.openroad import physyn
9
- from siliconcompiler.tools.openroad import place
10
- from siliconcompiler.tools.openroad import cts
11
- from siliconcompiler.tools.openroad import route
12
- from siliconcompiler.tools.openroad import dfm
13
- from siliconcompiler.tools.openroad import export as openroad_export
7
+ from siliconcompiler.tools.openroad import init_floorplan
8
+ from siliconcompiler.tools.openroad import macro_placement
9
+ from siliconcompiler.tools.openroad import endcap_tapcell_insertion
10
+ from siliconcompiler.tools.openroad import power_grid
11
+ from siliconcompiler.tools.openroad import pin_placement
12
+ from siliconcompiler.tools.openroad import global_placement
13
+ from siliconcompiler.tools.openroad import repair_design
14
+ from siliconcompiler.tools.openroad import detailed_placement
15
+ from siliconcompiler.tools.openroad import clock_tree_synthesis
16
+ from siliconcompiler.tools.openroad import repair_timing
17
+ from siliconcompiler.tools.openroad import fillercell_insertion
18
+ from siliconcompiler.tools.openroad import global_route
19
+ from siliconcompiler.tools.openroad import antenna_repair
20
+ from siliconcompiler.tools.openroad import detailed_route
21
+ from siliconcompiler.tools.openroad import fillmetal_insertion
22
+ from siliconcompiler.tools.openroad import write_data
14
23
  from siliconcompiler.tools.klayout import export as klayout_export
15
24
 
16
25
  from siliconcompiler.tools.builtin import minimum
@@ -70,33 +79,53 @@ def setup(flowname='asicflow',
70
79
  flow = siliconcompiler.Flow(flowname)
71
80
 
72
81
  # Linear flow, up until branch to run parallel verification steps.
73
- longpipe = ['syn',
74
- 'synmin',
75
- 'floorplan',
76
- 'floorplanmin',
77
- 'place',
78
- 'placemin',
79
- 'cts',
80
- 'ctsmin',
81
- 'route',
82
- 'routemin',
83
- 'dfm']
82
+ longpipe = [
83
+ 'syn',
84
+ 'syn.min',
85
+ 'floorplan.init',
86
+ 'floorplan.macro_placement',
87
+ 'floorplan.tapcell',
88
+ 'floorplan.power_grid',
89
+ 'floorplan.pin_placement',
90
+ 'floorplan.min',
91
+ 'place.global',
92
+ 'place.repair_design',
93
+ 'place.detailed',
94
+ 'place.min',
95
+ 'cts.clock_tree_synthesis',
96
+ 'cts.repair_timing',
97
+ 'cts.fillcell',
98
+ 'cts.min',
99
+ 'route.global',
100
+ 'route.antenna_repair',
101
+ 'route.detailed',
102
+ 'route.min',
103
+ 'dfm.metal_fill'
104
+ ]
84
105
 
85
106
  # step --> task
86
107
  tasks = {
87
108
  'syn': syn_asic,
88
- 'synmin': minimum,
89
- 'floorplan': floorplan,
90
- 'floorplanmin': minimum,
91
- 'physyn': physyn,
92
- 'physynmin': minimum,
93
- 'place': place,
94
- 'placemin': minimum,
95
- 'cts': cts,
96
- 'ctsmin': minimum,
97
- 'route': route,
98
- 'routemin': minimum,
99
- 'dfm': dfm
109
+ 'syn.min': minimum,
110
+ 'floorplan.init': init_floorplan,
111
+ 'floorplan.macro_placement': macro_placement,
112
+ 'floorplan.tapcell': endcap_tapcell_insertion,
113
+ 'floorplan.power_grid': power_grid,
114
+ 'floorplan.pin_placement': pin_placement,
115
+ 'floorplan.min': minimum,
116
+ 'place.global': global_placement,
117
+ 'place.repair_design': repair_design,
118
+ 'place.detailed': detailed_placement,
119
+ 'place.min': minimum,
120
+ 'cts.clock_tree_synthesis': clock_tree_synthesis,
121
+ 'cts.repair_timing': repair_timing,
122
+ 'cts.fillcell': fillercell_insertion,
123
+ 'cts.min': minimum,
124
+ 'route.global': global_route,
125
+ 'route.antenna_repair': antenna_repair,
126
+ 'route.detailed': detailed_route,
127
+ 'route.min': minimum,
128
+ 'dfm.metal_fill': fillmetal_insertion
100
129
  }
101
130
 
102
131
  np = {
@@ -114,7 +143,8 @@ def setup(flowname='asicflow',
114
143
  for step in longpipe:
115
144
  task = tasks[step]
116
145
  if task == minimum:
117
- if prevstep in np and np[prevstep] > 1:
146
+ np_step = prevstep.split('.')[0]
147
+ if np_step in np and np[np_step] > 1:
118
148
  flowpipe.append(step)
119
149
  else:
120
150
  flowpipe.append(step)
@@ -126,24 +156,30 @@ def setup(flowname='asicflow',
126
156
 
127
157
  # Programmatically build linear portion of flowgraph and fanin/fanout args
128
158
  prevstep = setup_multiple_frontends(flow)
159
+ prev_fanout = 1
129
160
  for step, task in flowtasks:
130
161
  fanout = 1
131
- if step in np:
132
- fanout = np[step]
162
+ np_step = step.split('.')[0]
163
+ if np_step in np and task != minimum:
164
+ fanout = np[np_step]
165
+
133
166
  # create nodes
134
167
  for index in range(fanout):
135
168
  # nodes
136
169
  flow.node(flowname, step, task, index=index)
137
170
 
171
+ # create edges
172
+ for index in range(fanout):
138
173
  # edges
174
+ fanin = prev_fanout
139
175
  if task == minimum:
140
- fanin = 1
141
- if prevstep in np:
142
- fanin = np[prevstep]
143
176
  for i in range(fanin):
144
177
  flow.edge(flowname, prevstep, step, tail_index=i)
145
178
  elif prevstep:
146
- flow.edge(flowname, prevstep, step, head_index=index)
179
+ if fanin == fanout:
180
+ flow.edge(flowname, prevstep, step, tail_index=index, head_index=index)
181
+ else:
182
+ flow.edge(flowname, prevstep, step, head_index=index)
147
183
 
148
184
  # metrics
149
185
  goal_metrics = ()
@@ -151,7 +187,11 @@ def setup(flowname='asicflow',
151
187
  if task in (syn_asic, ):
152
188
  goal_metrics = ('errors',)
153
189
  weight_metrics = ()
154
- elif task in (floorplan, physyn, place, cts, route, dfm):
190
+ elif task in (init_floorplan, macro_placement, endcap_tapcell_insertion,
191
+ power_grid, pin_placement, global_placement, repair_design,
192
+ detailed_placement, clock_tree_synthesis, repair_timing,
193
+ fillercell_insertion, global_route, antenna_repair, detailed_route,
194
+ fillmetal_insertion):
155
195
  goal_metrics = ('errors', 'setupwns', 'setuptns')
156
196
  weight_metrics = ('cellarea', 'peakpower', 'leakagepower')
157
197
 
@@ -160,12 +200,13 @@ def setup(flowname='asicflow',
160
200
  for metric in weight_metrics:
161
201
  flow.set('flowgraph', flowname, step, str(index), 'weight', metric, 1.0)
162
202
  prevstep = step
203
+ prev_fanout = fanout
163
204
 
164
205
  # add write information steps
165
- flow.node(flowname, 'write_gds', klayout_export)
166
- flow.edge(flowname, prevstep, 'write_gds')
167
- flow.node(flowname, 'write_data', openroad_export)
168
- flow.edge(flowname, prevstep, 'write_data')
206
+ flow.node(flowname, 'write.gds', klayout_export)
207
+ flow.edge(flowname, prevstep, 'write.gds')
208
+ flow.node(flowname, 'write.views', write_data)
209
+ flow.edge(flowname, prevstep, 'write.views')
169
210
 
170
211
  return flow
171
212
 
@@ -1,12 +1,12 @@
1
1
  import siliconcompiler
2
2
  from siliconcompiler import SiliconCompilerError
3
- from siliconcompiler.targets import freepdk45_demo
4
3
 
5
4
 
6
5
  ############################################################################
7
6
  # DOCS
8
7
  ############################################################################
9
8
  def make_docs(chip):
9
+ from siliconcompiler.targets import freepdk45_demo
10
10
  chip.use(freepdk45_demo)
11
11
  return setup(filetype='gds', showtools=chip._showtools, np=3)
12
12
 
@@ -0,0 +1,5 @@
1
+ def get_libs():
2
+ '''
3
+ Returns a dict of builtin libraries
4
+ '''
5
+ return {}
@@ -0,0 +1,199 @@
1
+ class Optimizer:
2
+ def __init__(self, chip):
3
+ self._chip = chip
4
+
5
+ self._parameters = {}
6
+ self._goals = {}
7
+ self._assertions = {}
8
+
9
+ self.__results = []
10
+
11
+ def __generate_print_name(self, key, step, index):
12
+ name = f'[{",".join(key)}]'
13
+
14
+ node_name = None
15
+ if step is not None:
16
+ node_name = step
17
+
18
+ if index is not None:
19
+ node_name += f'{index}'
20
+
21
+ if node_name:
22
+ name += f' ({node_name})'
23
+
24
+ return name
25
+
26
+ def __generate_param_name(self, key, step, index):
27
+ name = ",".join(key)
28
+
29
+ if step is not None:
30
+ name += f'-step-{step}'
31
+
32
+ if index is not None:
33
+ name += f'-index-{index}'
34
+
35
+ return name
36
+
37
+ def add_parameter(self, key, values, value_type=None, step=None, index=None):
38
+ if value_type is None:
39
+ value_type = self._chip.get(*key, field='type')
40
+ if value_type.startswith('['):
41
+ value_type = value_type[1:-1]
42
+ elif value_type.startswith('('):
43
+ value_type = value_type[1:-1].split(",")
44
+ value_type = [value.strip() for value in value_type]
45
+ if not all([value == value_type[0] for value in value_type]):
46
+ raise ValueError("Cannot support unequal tuples")
47
+ value_type = value_type[0]
48
+
49
+ if value_type not in ('float', 'int', 'bool', 'enum', 'str'):
50
+ raise ValueError(f"{value_type} is not supported")
51
+
52
+ if value_type in ('float', 'int'):
53
+ if 'max' not in values:
54
+ raise ValueError("value must have a max key")
55
+ if 'min' not in values:
56
+ raise ValueError("value must have a min key")
57
+ values = [values["min"], values["max"]]
58
+ elif value_type in ('enum', 'str', 'bool'):
59
+ if not isinstance(values, (tuple, list, set)):
60
+ raise ValueError("value must be a list")
61
+ if value_type == 'str':
62
+ value_type = 'enum'
63
+
64
+ if value_type == 'bool' and not values:
65
+ values = [True, False]
66
+
67
+ self._parameters["param-" + self.__generate_param_name(key, step, index)] = {
68
+ "print": self.__generate_print_name(key, step, index),
69
+ "key": tuple(key),
70
+ "type": value_type,
71
+ "values": tuple(values),
72
+ "step": step,
73
+ "index": index
74
+ }
75
+
76
+ def _set_parameter(self, parameter, value, chip, flow_prefix=None):
77
+ param_entry = self._parameters[parameter]
78
+
79
+ self._chip.logger.info(f' Setting {param_entry["print"]} = {value}')
80
+ if param_entry["step"]:
81
+ if not flow_prefix:
82
+ flow_prefix = ""
83
+ step = f'{flow_prefix}{param_entry["step"]}'
84
+ else:
85
+ step = param_entry["step"]
86
+
87
+ key_type = chip.get(*param_entry["key"], field='type')
88
+ if key_type[0] == "(":
89
+ key_type = key_type[1:-1].split(",")
90
+ value = len(key_type) * [value]
91
+
92
+ chip.set(
93
+ *param_entry["key"],
94
+ value,
95
+ step=step,
96
+ index=param_entry["index"])
97
+
98
+ def add_assertion(self, key, criteria, step=None, index=None):
99
+ if not callable(criteria):
100
+ raise ValueError('criteria must be a function')
101
+
102
+ if not step:
103
+ raise ValueError('step is required')
104
+
105
+ if not index:
106
+ raise ValueError('index is required')
107
+
108
+ self._assertions["assert-" + self.__generate_param_name(key, step, index)] = {
109
+ "print": self.__generate_print_name(key, step, index),
110
+ "key": tuple(key),
111
+ "criteria": criteria,
112
+ "step": step,
113
+ "index": index
114
+ }
115
+
116
+ def _check_assertions(self, chip, step_prefix):
117
+ if not step_prefix:
118
+ step_prefix = ""
119
+
120
+ for info in self._assertions.values():
121
+ value = chip.get(
122
+ *info["key"],
123
+ step=f'{step_prefix}{info["step"]}',
124
+ index=info["index"])
125
+ if not info["criteria"](value):
126
+ self._chip.logger.error(f"Failed to meet assertion: {info['print']} with {value}")
127
+ return False
128
+
129
+ return True
130
+
131
+ def add_goal(self, key, goal, stop_goal=None, step=None, index=None):
132
+ if goal not in ('min', 'max'):
133
+ raise ValueError(f"{goal} is not supported")
134
+
135
+ if not step:
136
+ raise ValueError('step is required')
137
+
138
+ if not index:
139
+ raise ValueError('index is required')
140
+
141
+ self._goals["goal-" + self.__generate_param_name(key, step, index)] = {
142
+ "print": self.__generate_print_name(key, step, index),
143
+ "key": tuple(key),
144
+ "goal": goal,
145
+ "stop": stop_goal,
146
+ "step": step,
147
+ "index": index
148
+ }
149
+
150
+ def _check_stop_goal(self, measurements):
151
+ cont = []
152
+
153
+ for param, info in self._goals.items():
154
+ if info["stop"] is None:
155
+ continue
156
+
157
+ if param not in measurements:
158
+ cont.append(False)
159
+ continue
160
+
161
+ if info["goal"] == "min":
162
+ if measurements[param] <= info["stop"]:
163
+ cont.append(True)
164
+ elif info["goal"] == "max":
165
+ if measurements[param] >= info["stop"]:
166
+ cont.append(True)
167
+
168
+ if not cont:
169
+ return False
170
+
171
+ return all(cont)
172
+
173
+ def run(self, experiments=None, parallel=None):
174
+ raise NotImplementedError
175
+
176
+ def _clear_results(self):
177
+ self.__results.clear()
178
+
179
+ def _add_result(self, parameters, measurements):
180
+ self.__results.append({
181
+ "parameters": parameters,
182
+ "measurements": measurements
183
+ })
184
+
185
+ def report(self, count=None):
186
+ for n, result in enumerate(self.__results):
187
+ if count and n >= count:
188
+ return
189
+
190
+ self._chip.logger.info(f"Result {n+1} / {len(self.__results)}:")
191
+ self._chip.logger.info(" Parameters:")
192
+ for param_name, param_key in result["parameters"].items():
193
+ param_print = self._parameters[param_name]['print']
194
+ self._chip.logger.info(f" {param_print} = {param_key}")
195
+
196
+ self._chip.logger.info(" Measurements:")
197
+ for meas_name, meas_key in result["measurements"].metrics.items():
198
+ goal_print = self._goals[meas_name]['print']
199
+ self._chip.logger.info(f" {goal_print} = {meas_key.value}")
@@ -0,0 +1,259 @@
1
+ import logging
2
+ import uuid
3
+ import math
4
+ from siliconcompiler import Chip
5
+ from siliconcompiler.optimizer import Optimizer
6
+ from siliconcompiler.flowgraph import _get_flowgraph_nodes
7
+
8
+ try:
9
+ from vizier.service import clients as vz_clients
10
+ from vizier.service import pyvizier as vz
11
+
12
+ from jax import config
13
+ config.update("jax_enable_x64", True)
14
+ _has_vizier = True
15
+
16
+ logging.getLogger('absl').setLevel(logging.CRITICAL)
17
+ logging.getLogger('jax').setLevel(logging.CRITICAL)
18
+ except ModuleNotFoundError:
19
+ _has_vizier = False
20
+
21
+
22
+ class VizierOptimizier(Optimizer):
23
+ def __init__(self, chip):
24
+ if not _has_vizier:
25
+ raise RuntimeError("vizier is not available")
26
+
27
+ super().__init__(chip)
28
+
29
+ self.__problem = None
30
+ self.__study = None
31
+
32
+ self.__owner = chip.design
33
+ self.__experiment_rounds = None
34
+ self.__parallel_experiment = None
35
+
36
+ def __init_parameters(self):
37
+ search_space = self.__problem.search_space.root
38
+ for name, info in self._parameters.items():
39
+ values = info["values"]
40
+ if info["type"] == 'float':
41
+ search_space.add_float_param(name, values[0], values[1])
42
+ elif info["type"] == 'int':
43
+ search_space.add_int_param(name, values[0], values[1])
44
+ elif info["type"] == 'bool':
45
+ search_space.add_discrete_param(name, values)
46
+ elif info["type"] == 'enum':
47
+ if any([isinstance(v, str) for v in values]):
48
+ search_space.add_categorical_param(name, values)
49
+ else:
50
+ search_space.add_discrete_param(name, values)
51
+
52
+ def __init_goals(self):
53
+ metric_information = self.__problem.metric_information
54
+
55
+ for name, info in self._goals.items():
56
+
57
+ vz_goal = None
58
+ if info["goal"] == 'max':
59
+ vz_goal = vz.ObjectiveMetricGoal.MAXIMIZE
60
+ elif info["goal"] == 'min':
61
+ vz_goal = vz.ObjectiveMetricGoal.MINIMIZE
62
+ else:
63
+ raise ValueError(f'{info["goal"]} is not a supported goal')
64
+
65
+ metric_information.append(vz.MetricInformation(name, goal=vz_goal))
66
+
67
+ def __init_study(self):
68
+ # Setup client and begin optimization
69
+ # Vizier Service will be implicitly created
70
+ study_config = vz.StudyConfig.from_problem(self.__problem)
71
+ study_config.algorithm = 'DEFAULT'
72
+ if len(self._goals) > 1:
73
+ if self.__parallel_experiment == 1:
74
+ study_config.algorithm = 'GAUSSIAN_PROCESS_BANDIT'
75
+ else:
76
+ study_config.algorithm = 'QUASI_RANDOM_SEARCH'
77
+
78
+ self.__study = vz_clients.Study.from_study_config(
79
+ study_config,
80
+ owner=self.__owner,
81
+ study_id=uuid.uuid4().hex)
82
+
83
+ def run(self, experiments=None, parallel=None):
84
+ if not experiments:
85
+ experiments = 10 * len(self._parameters)
86
+ self._chip.logger.debug(f'Setting number of optimizer experiments to {experiments}')
87
+
88
+ if not parallel:
89
+ parallel = 1
90
+
91
+ self.__parallel_experiment = parallel
92
+
93
+ # Algorithm, search space, and metrics.
94
+ self.__problem = vz.ProblemStatement()
95
+
96
+ self._clear_results()
97
+
98
+ self.__init_parameters()
99
+ self.__init_goals()
100
+
101
+ self.__init_study()
102
+
103
+ self.__experiment_rounds = int(math.ceil(float(experiments) / parallel))
104
+ accept = True
105
+ try:
106
+ for n in range(self.__experiment_rounds):
107
+ if self.__run_round(n):
108
+ break
109
+ except KeyboardInterrupt:
110
+ pass
111
+ except Exception as e:
112
+ self._chip.logger.error(f"{e}")
113
+ accept = False
114
+ finally:
115
+ if accept:
116
+ self.__record_optimal()
117
+
118
+ self.__study.delete()
119
+ self.__study = None
120
+
121
+ def __run_round(self, experiment_round):
122
+ # create a new chip with a copy of its schema
123
+ chip = Chip(self._chip.design)
124
+ chip.schema = self._chip.schema.copy()
125
+
126
+ suggestions = self.__setup_round(experiment_round, chip)
127
+
128
+ # Start run
129
+ try:
130
+ chip.logger.info(
131
+ f"Starting optimizer run ({experiment_round+1} / {self.__experiment_rounds})")
132
+ chip.run()
133
+ except KeyboardInterrupt:
134
+ raise
135
+ except Exception as e:
136
+ chip.logger.error(f"{e}")
137
+
138
+ return self.__record_round(chip, suggestions)
139
+
140
+ def __setup_round(self, experiment_round, chip):
141
+ org_flow = self._chip.get("option", "flow")
142
+ org_jobname = self._chip.get("option", "jobname")
143
+
144
+ jobname = f"{org_jobname}-{org_flow}-{experiment_round+1}"
145
+
146
+ flow_map = {}
147
+
148
+ if self.__parallel_experiment > 1:
149
+ flow = f'optimize_{org_flow}'
150
+ # Create new graph
151
+ for m in range(self.__parallel_experiment):
152
+ graph_name = f'opt{m+1}'
153
+ flow_map[m] = {
154
+ "name": f'{jobname}/{graph_name}',
155
+ "prefix": f"{graph_name}.",
156
+ "suggestion": None
157
+ }
158
+ chip.graph(flow, org_flow, name=graph_name)
159
+
160
+ # Complete nodes
161
+ nodes = _get_flowgraph_nodes(chip, org_flow)
162
+ for step, _ in list(nodes):
163
+ nodes.append((step, None))
164
+ nodes = set(nodes)
165
+
166
+ # Forward node specific values
167
+ for key in chip.schema.allkeys():
168
+ if key[0] == 'history':
169
+ continue
170
+
171
+ for value, step, index in chip.schema._getvals(*key):
172
+ node = (step, index)
173
+
174
+ if node in nodes:
175
+ for info in flow_map.values():
176
+ chip.set(
177
+ *key,
178
+ value,
179
+ step=f'{info["prefix"]}{step}',
180
+ index=index)
181
+ else:
182
+ flow = org_flow
183
+ flow_map[0] = {
184
+ "name": jobname,
185
+ "prefix": "",
186
+ "suggestion": None
187
+ }
188
+
189
+ # Setup each experiment
190
+ for m, suggestion in enumerate(self.__study.suggest(count=self.__parallel_experiment)):
191
+ self._chip.logger.info(f'Setting parameters for {flow_map[m]["name"]}')
192
+ flow_map[m]["suggestion"] = suggestion
193
+
194
+ for param_name, param_value in suggestion.parameters.items():
195
+ self._set_parameter(
196
+ param_name,
197
+ param_value,
198
+ chip,
199
+ flow_prefix=flow_map[m]["prefix"])
200
+
201
+ chip.set('option', 'jobname', jobname)
202
+ chip.set('option', 'flow', flow)
203
+ chip.set('option', 'quiet', True)
204
+
205
+ steps = set()
206
+ for info in list(self._goals.values()) + list(self._assertions.values()):
207
+ for flow in flow_map.values():
208
+ steps.add(f'{flow["prefix"]}{info["step"]}')
209
+ chip.set('option', 'to', steps)
210
+
211
+ return flow_map
212
+
213
+ def __record_round(self, chip, suggestions):
214
+ jobname = chip.get('option', 'jobname')
215
+
216
+ # Record history
217
+ self._chip.schema.cfg['history'][jobname] = chip.schema.history(jobname).cfg
218
+
219
+ stop = False
220
+
221
+ for trial_entry in suggestions.values():
222
+ trial_suggestion = trial_entry['suggestion']
223
+
224
+ measurement = {}
225
+ self._chip.logger.info(f'Measuring {trial_entry["name"]}')
226
+ for meas_name, meas_entry in self._goals.items():
227
+ measurement[meas_name] = chip.get(
228
+ *meas_entry["key"],
229
+ step=f'{trial_entry["prefix"]}{meas_entry["step"]}',
230
+ index=meas_entry["index"])
231
+
232
+ self._chip.logger.info(f' Measured {meas_entry["print"]} = '
233
+ f'{measurement[meas_name]}')
234
+
235
+ failed = None
236
+ if any([value is None for value in measurement.values()]):
237
+ failed = "Did not record measurement goal"
238
+ elif not self._check_assertions(chip, trial_entry["prefix"]):
239
+ failed = "Failed to meet assertions"
240
+
241
+ if failed:
242
+ self._chip.logger.error(f'{trial_entry["name"]} failed: {failed}')
243
+ trial_suggestion.complete(vz.Measurement(),
244
+ infeasible_reason=failed)
245
+ else:
246
+ trial_suggestion.complete(vz.Measurement(measurement))
247
+ stop |= self._check_stop_goal(measurement)
248
+
249
+ return stop
250
+
251
+ def __record_optimal(self):
252
+ optimal_trials = list(self.__study.optimal_trials())
253
+ for n, optimal_trial in enumerate(optimal_trials):
254
+ optimal_trial = optimal_trial.materialize()
255
+
256
+ self._add_result(
257
+ optimal_trial.parameters,
258
+ optimal_trial.final_measurement
259
+ )
@@ -0,0 +1,5 @@
1
+ def get_pdks():
2
+ '''
3
+ Returns a dict of builtin pdks
4
+ '''
5
+ return {}
@@ -34,3 +34,14 @@ class JobStatus():
34
34
  TIMEOUT = "timeout"
35
35
 
36
36
  UNKNOWN = "unknown"
37
+
38
+
39
+ from siliconcompiler.remote.client import Client, ConfigureClient # noqa E402
40
+
41
+
42
+ __all__ = [
43
+ "NodeStatus",
44
+ "JobStatus",
45
+ "Client",
46
+ "ConfigureClient"
47
+ ]