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.
- siliconcompiler/_metadata.py +1 -1
- siliconcompiler/apps/__init__.py +26 -0
- siliconcompiler/apps/sc_remote.py +15 -14
- siliconcompiler/apps/sc_show.py +5 -5
- siliconcompiler/apps/utils/replay.py +194 -0
- siliconcompiler/checklists/__init__.py +12 -0
- siliconcompiler/core.py +89 -22
- siliconcompiler/flows/__init__.py +34 -0
- siliconcompiler/flows/_common.py +11 -13
- siliconcompiler/flows/asicflow.py +83 -42
- siliconcompiler/flows/showflow.py +1 -1
- siliconcompiler/libs/__init__.py +5 -0
- siliconcompiler/optimizer/__init__.py +199 -0
- siliconcompiler/optimizer/vizier.py +259 -0
- siliconcompiler/pdks/__init__.py +5 -0
- siliconcompiler/remote/__init__.py +11 -0
- siliconcompiler/remote/client.py +753 -815
- siliconcompiler/report/report.py +2 -0
- siliconcompiler/report/summary_table.py +1 -1
- siliconcompiler/scheduler/__init__.py +118 -58
- siliconcompiler/scheduler/send_messages.py +1 -1
- siliconcompiler/schema/schema_cfg.py +16 -4
- siliconcompiler/schema/schema_obj.py +29 -10
- siliconcompiler/schema/utils.py +2 -0
- siliconcompiler/sphinx_ext/__init__.py +85 -0
- siliconcompiler/sphinx_ext/dynamicgen.py +19 -34
- siliconcompiler/sphinx_ext/schemagen.py +3 -2
- siliconcompiler/targets/__init__.py +26 -0
- siliconcompiler/targets/gf180_demo.py +3 -3
- siliconcompiler/templates/replay/replay.py.j2 +62 -0
- siliconcompiler/templates/replay/requirements.txt +7 -0
- siliconcompiler/templates/replay/setup.sh +130 -0
- siliconcompiler/tools/__init__.py +60 -0
- siliconcompiler/tools/_common/__init__.py +15 -1
- siliconcompiler/tools/_common/asic.py +17 -9
- siliconcompiler/tools/builtin/concatenate.py +1 -1
- siliconcompiler/tools/ghdl/ghdl.py +1 -2
- siliconcompiler/tools/klayout/convert_drc_db.py +1 -1
- siliconcompiler/tools/klayout/drc.py +1 -1
- siliconcompiler/tools/klayout/export.py +8 -1
- siliconcompiler/tools/klayout/klayout.py +2 -2
- siliconcompiler/tools/klayout/klayout_convert_drc_db.py +2 -2
- siliconcompiler/tools/klayout/klayout_export.py +7 -5
- siliconcompiler/tools/klayout/klayout_operations.py +4 -3
- siliconcompiler/tools/klayout/klayout_show.py +3 -2
- siliconcompiler/tools/klayout/klayout_utils.py +1 -1
- siliconcompiler/tools/klayout/operations.py +8 -0
- siliconcompiler/tools/klayout/screenshot.py +6 -1
- siliconcompiler/tools/klayout/show.py +8 -1
- siliconcompiler/tools/magic/magic.py +1 -1
- siliconcompiler/tools/openroad/__init__.py +103 -0
- siliconcompiler/tools/openroad/{openroad.py → _apr.py} +415 -423
- siliconcompiler/tools/openroad/antenna_repair.py +78 -0
- siliconcompiler/tools/openroad/clock_tree_synthesis.py +64 -0
- siliconcompiler/tools/openroad/detailed_placement.py +59 -0
- siliconcompiler/tools/openroad/detailed_route.py +62 -0
- siliconcompiler/tools/openroad/endcap_tapcell_insertion.py +52 -0
- siliconcompiler/tools/openroad/fillercell_insertion.py +58 -0
- siliconcompiler/tools/openroad/{dfm.py → fillmetal_insertion.py} +35 -19
- siliconcompiler/tools/openroad/global_placement.py +58 -0
- siliconcompiler/tools/openroad/global_route.py +63 -0
- siliconcompiler/tools/openroad/init_floorplan.py +103 -0
- siliconcompiler/tools/openroad/macro_placement.py +65 -0
- siliconcompiler/tools/openroad/metrics.py +23 -8
- siliconcompiler/tools/openroad/pin_placement.py +56 -0
- siliconcompiler/tools/openroad/power_grid.py +65 -0
- siliconcompiler/tools/openroad/rcx_bench.py +7 -4
- siliconcompiler/tools/openroad/rcx_extract.py +2 -1
- siliconcompiler/tools/openroad/rdlroute.py +4 -4
- siliconcompiler/tools/openroad/repair_design.py +59 -0
- siliconcompiler/tools/openroad/repair_timing.py +63 -0
- siliconcompiler/tools/openroad/screenshot.py +9 -20
- siliconcompiler/tools/openroad/scripts/apr/postamble.tcl +44 -0
- siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +95 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_antenna_repair.tcl +51 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_clock_tree_synthesis.tcl +66 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_detailed_placement.tcl +41 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +71 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_endcap_tapcell_insertion.tcl +55 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_fillercell_insertion.tcl +27 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_fillmetal_insertion.tcl +36 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_global_placement.tcl +26 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +61 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +333 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_macro_placement.tcl +123 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_metrics.tcl +22 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_pin_placement.tcl +41 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_power_grid.tcl +60 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_repair_design.tcl +68 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_repair_timing.tcl +83 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_write_data.tcl +125 -0
- siliconcompiler/tools/openroad/scripts/common/debugging.tcl +28 -0
- siliconcompiler/tools/openroad/scripts/common/procs.tcl +727 -0
- siliconcompiler/tools/openroad/scripts/common/read_input_files.tcl +59 -0
- siliconcompiler/tools/openroad/scripts/common/read_liberty.tcl +20 -0
- siliconcompiler/tools/openroad/scripts/common/read_timing_constraints.tcl +16 -0
- siliconcompiler/tools/openroad/scripts/common/reports.tcl +180 -0
- siliconcompiler/tools/openroad/scripts/common/screenshot.tcl +18 -0
- siliconcompiler/tools/openroad/scripts/common/write_images.tcl +395 -0
- siliconcompiler/tools/openroad/scripts/{sc_rcx_bench.tcl → rcx/sc_rcx_bench.tcl} +5 -5
- siliconcompiler/tools/openroad/scripts/{sc_rcx_extract.tcl → rcx/sc_rcx_extract.tcl} +0 -0
- siliconcompiler/tools/openroad/scripts/sc_rcx.tcl +5 -16
- siliconcompiler/tools/openroad/scripts/sc_rdlroute.tcl +51 -51
- siliconcompiler/tools/openroad/scripts/sc_show.tcl +110 -0
- siliconcompiler/tools/openroad/show.py +28 -23
- siliconcompiler/tools/openroad/{export.py → write_data.py} +31 -26
- siliconcompiler/tools/opensta/__init__.py +2 -2
- siliconcompiler/tools/opensta/check_library.py +27 -0
- siliconcompiler/tools/opensta/scripts/sc_check_library.tcl +255 -0
- siliconcompiler/tools/opensta/scripts/sc_timing.tcl +1 -1
- siliconcompiler/tools/sv2v/sv2v.py +1 -2
- siliconcompiler/tools/verilator/verilator.py +6 -7
- siliconcompiler/tools/vivado/vivado.py +1 -1
- siliconcompiler/tools/yosys/__init__.py +149 -0
- siliconcompiler/tools/yosys/lec.py +22 -9
- siliconcompiler/tools/yosys/sc_lec.tcl +94 -49
- siliconcompiler/tools/yosys/sc_syn.tcl +1 -0
- siliconcompiler/tools/yosys/screenshot.py +2 -2
- siliconcompiler/tools/yosys/syn_asic.py +105 -74
- siliconcompiler/tools/yosys/syn_asic.tcl +58 -12
- siliconcompiler/tools/yosys/syn_fpga.py +2 -3
- siliconcompiler/tools/yosys/syn_fpga.tcl +26 -19
- siliconcompiler/toolscripts/_tools.json +5 -5
- siliconcompiler/utils/__init__.py +7 -3
- {siliconcompiler-0.28.9.dist-info → siliconcompiler-0.29.1.dist-info}/METADATA +22 -17
- {siliconcompiler-0.28.9.dist-info → siliconcompiler-0.29.1.dist-info}/RECORD +131 -114
- {siliconcompiler-0.28.9.dist-info → siliconcompiler-0.29.1.dist-info}/WHEEL +1 -1
- {siliconcompiler-0.28.9.dist-info → siliconcompiler-0.29.1.dist-info}/entry_points.txt +13 -0
- siliconcompiler/libs/asap7sc7p5t.py +0 -8
- siliconcompiler/libs/gf180mcu.py +0 -8
- siliconcompiler/libs/interposer.py +0 -8
- siliconcompiler/libs/nangate45.py +0 -8
- siliconcompiler/libs/sg13g2_stdcell.py +0 -8
- siliconcompiler/libs/sky130hd.py +0 -8
- siliconcompiler/libs/sky130io.py +0 -8
- siliconcompiler/pdks/asap7.py +0 -8
- siliconcompiler/pdks/freepdk45.py +0 -8
- siliconcompiler/pdks/gf180.py +0 -8
- siliconcompiler/pdks/ihp130.py +0 -8
- siliconcompiler/pdks/interposer.py +0 -8
- siliconcompiler/pdks/skywater130.py +0 -8
- siliconcompiler/tools/openroad/cts.py +0 -45
- siliconcompiler/tools/openroad/floorplan.py +0 -75
- siliconcompiler/tools/openroad/physyn.py +0 -27
- siliconcompiler/tools/openroad/place.py +0 -41
- siliconcompiler/tools/openroad/route.py +0 -45
- siliconcompiler/tools/openroad/scripts/__init__.py +0 -0
- siliconcompiler/tools/openroad/scripts/sc_apr.tcl +0 -514
- siliconcompiler/tools/openroad/scripts/sc_cts.tcl +0 -68
- siliconcompiler/tools/openroad/scripts/sc_dfm.tcl +0 -22
- siliconcompiler/tools/openroad/scripts/sc_export.tcl +0 -100
- siliconcompiler/tools/openroad/scripts/sc_floorplan.tcl +0 -456
- siliconcompiler/tools/openroad/scripts/sc_metrics.tcl +0 -1
- siliconcompiler/tools/openroad/scripts/sc_physyn.tcl +0 -6
- siliconcompiler/tools/openroad/scripts/sc_place.tcl +0 -84
- siliconcompiler/tools/openroad/scripts/sc_procs.tcl +0 -494
- siliconcompiler/tools/openroad/scripts/sc_report.tcl +0 -189
- siliconcompiler/tools/openroad/scripts/sc_route.tcl +0 -143
- siliconcompiler/tools/openroad/scripts/sc_screenshot.tcl +0 -18
- siliconcompiler/tools/openroad/scripts/sc_write_images.tcl +0 -393
- siliconcompiler/tools/yosys/yosys.py +0 -148
- /siliconcompiler/tools/openroad/scripts/{sc_write.tcl → common/write_data.tcl} +0 -0
- {siliconcompiler-0.28.9.dist-info → siliconcompiler-0.29.1.dist-info}/LICENSE +0 -0
- {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
|
|
8
|
-
from siliconcompiler.tools.openroad import
|
|
9
|
-
from siliconcompiler.tools.openroad import
|
|
10
|
-
from siliconcompiler.tools.openroad import
|
|
11
|
-
from siliconcompiler.tools.openroad import
|
|
12
|
-
from siliconcompiler.tools.openroad import
|
|
13
|
-
from siliconcompiler.tools.openroad import
|
|
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 = [
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
'
|
|
89
|
-
'floorplan':
|
|
90
|
-
'
|
|
91
|
-
'
|
|
92
|
-
'
|
|
93
|
-
'
|
|
94
|
-
'
|
|
95
|
-
'
|
|
96
|
-
'
|
|
97
|
-
'
|
|
98
|
-
'
|
|
99
|
-
'
|
|
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
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
-
|
|
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 (
|
|
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, '
|
|
166
|
-
flow.edge(flowname, prevstep, '
|
|
167
|
-
flow.node(flowname, '
|
|
168
|
-
flow.edge(flowname, prevstep, '
|
|
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
|
|
siliconcompiler/libs/__init__.py
CHANGED
|
@@ -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
|
+
)
|
siliconcompiler/pdks/__init__.py
CHANGED