siliconcompiler 0.35.4__py3-none-any.whl → 0.36.0__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/constraints/__init__.py +4 -1
- siliconcompiler/constraints/asic_timing.py +230 -38
- siliconcompiler/constraints/fpga_timing.py +209 -14
- siliconcompiler/constraints/timing_mode.py +82 -0
- siliconcompiler/data/templates/tcl/manifest.tcl.j2 +0 -6
- siliconcompiler/flowgraph.py +95 -42
- siliconcompiler/flows/generate_openroad_rcx.py +2 -2
- siliconcompiler/flows/highresscreenshotflow.py +37 -0
- siliconcompiler/library.py +2 -1
- siliconcompiler/package/__init__.py +39 -45
- siliconcompiler/project.py +4 -1
- siliconcompiler/scheduler/scheduler.py +64 -35
- siliconcompiler/scheduler/schedulernode.py +5 -2
- siliconcompiler/scheduler/slurm.py +7 -6
- siliconcompiler/scheduler/taskscheduler.py +19 -16
- siliconcompiler/schema/_metadata.py +1 -1
- siliconcompiler/schema/namedschema.py +2 -4
- siliconcompiler/schema_support/cmdlineschema.py +0 -3
- siliconcompiler/schema_support/dependencyschema.py +0 -6
- siliconcompiler/schema_support/record.py +4 -3
- siliconcompiler/tool.py +58 -27
- siliconcompiler/tools/_common/tcl/sc_schema_access.tcl +0 -6
- siliconcompiler/tools/keplerformal/__init__.py +7 -0
- siliconcompiler/tools/keplerformal/lec.py +112 -0
- siliconcompiler/tools/klayout/screenshot.py +66 -1
- siliconcompiler/tools/klayout/scripts/klayout_export.py +10 -40
- siliconcompiler/tools/klayout/scripts/klayout_show.py +4 -4
- siliconcompiler/tools/klayout/scripts/klayout_utils.py +13 -1
- siliconcompiler/tools/montage/tile.py +26 -12
- siliconcompiler/tools/openroad/_apr.py +26 -10
- siliconcompiler/tools/openroad/init_floorplan.py +5 -2
- siliconcompiler/tools/openroad/power_grid_analysis.py +1 -1
- siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +49 -14
- siliconcompiler/tools/openroad/scripts/apr/sc_irdrop.tcl +6 -4
- siliconcompiler/tools/openroad/scripts/apr/sc_write_data.tcl +4 -4
- siliconcompiler/tools/openroad/scripts/common/procs.tcl +14 -5
- siliconcompiler/tools/openroad/scripts/common/read_liberty.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/common/reports.tcl +6 -3
- siliconcompiler/tools/openroad/scripts/common/screenshot.tcl +1 -1
- siliconcompiler/tools/openroad/scripts/common/write_data_physical.tcl +8 -0
- siliconcompiler/tools/openroad/scripts/common/write_images.tcl +16 -12
- siliconcompiler/tools/openroad/scripts/sc_rdlroute.tcl +3 -1
- siliconcompiler/tools/openroad/write_data.py +2 -2
- siliconcompiler/tools/opensta/scripts/sc_check_library.tcl +2 -2
- siliconcompiler/tools/opensta/scripts/sc_report_libraries.tcl +2 -2
- siliconcompiler/tools/opensta/scripts/sc_timing.tcl +12 -14
- siliconcompiler/tools/opensta/timing.py +6 -2
- siliconcompiler/toolscripts/_tools.json +9 -4
- siliconcompiler/toolscripts/ubuntu22/install-keplerformal.sh +72 -0
- siliconcompiler/toolscripts/ubuntu24/install-keplerformal.sh +72 -0
- siliconcompiler/utils/multiprocessing.py +11 -0
- siliconcompiler/utils/settings.py +70 -49
- {siliconcompiler-0.35.4.dist-info → siliconcompiler-0.36.0.dist-info}/METADATA +3 -3
- {siliconcompiler-0.35.4.dist-info → siliconcompiler-0.36.0.dist-info}/RECORD +59 -53
- {siliconcompiler-0.35.4.dist-info → siliconcompiler-0.36.0.dist-info}/WHEEL +0 -0
- {siliconcompiler-0.35.4.dist-info → siliconcompiler-0.36.0.dist-info}/entry_points.txt +0 -0
- {siliconcompiler-0.35.4.dist-info → siliconcompiler-0.36.0.dist-info}/licenses/LICENSE +0 -0
- {siliconcompiler-0.35.4.dist-info → siliconcompiler-0.36.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import os.path
|
|
2
|
+
|
|
3
|
+
from siliconcompiler import Task
|
|
4
|
+
from siliconcompiler.utils import sc_open
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class LECTask(Task):
|
|
8
|
+
"""LEC task using Kepler-formal for logical equivalence checking."""
|
|
9
|
+
def tool(self) -> str:
|
|
10
|
+
return "kepler-formal"
|
|
11
|
+
|
|
12
|
+
def task(self) -> str:
|
|
13
|
+
return "lec"
|
|
14
|
+
|
|
15
|
+
def setup(self) -> None:
|
|
16
|
+
super().setup()
|
|
17
|
+
|
|
18
|
+
self.set_exe("kepler-formal")
|
|
19
|
+
|
|
20
|
+
# Log file handled by kepler-formal config
|
|
21
|
+
self.set_logdestination("stdout", "none")
|
|
22
|
+
self.set_logdestination("stderr", "none")
|
|
23
|
+
|
|
24
|
+
if f"{self.design_topmodule}.lec.vg" in self.get_files_from_input_nodes():
|
|
25
|
+
inputnodes = self.get_files_from_input_nodes()[f"{self.design_topmodule}.lec.vg"]
|
|
26
|
+
if len(inputnodes) != 2:
|
|
27
|
+
raise ValueError("LEC requires exactly two input netlists for comparison.")
|
|
28
|
+
for node in inputnodes:
|
|
29
|
+
self.add_input_file(
|
|
30
|
+
self.compute_input_file_node_name(f"{self.design_topmodule}.lec.vg", *node))
|
|
31
|
+
|
|
32
|
+
scenarios = self.project.constraint.timing.get_scenario()
|
|
33
|
+
if not scenarios:
|
|
34
|
+
raise ValueError("LEC requires at least one timing scenario to determine "
|
|
35
|
+
"library corners.")
|
|
36
|
+
scenario = list(scenarios.values())[0]
|
|
37
|
+
libcorners = scenario.get_libcorner(self.step, self.index)
|
|
38
|
+
delay_model = self.project.get("asic", "delaymodel")
|
|
39
|
+
for asiclib in self.project.get("asic", "asiclib"):
|
|
40
|
+
lib = self.project.get("library", asiclib, field="schema")
|
|
41
|
+
for corner in libcorners:
|
|
42
|
+
if not lib.valid("asic", "libcornerfileset", corner, delay_model):
|
|
43
|
+
continue
|
|
44
|
+
self.add_required_key(lib, "asic", "libcornerfileset", corner, delay_model)
|
|
45
|
+
for fileset in lib.get("asic", "libcornerfileset", corner, delay_model):
|
|
46
|
+
self.add_required_key(lib, "fileset", fileset, "file", "liberty")
|
|
47
|
+
|
|
48
|
+
def __config_file(self) -> str:
|
|
49
|
+
return "lec.yaml"
|
|
50
|
+
|
|
51
|
+
def pre_process(self):
|
|
52
|
+
super().pre_process()
|
|
53
|
+
|
|
54
|
+
with open(self.__config_file(), "w") as f:
|
|
55
|
+
f.write("format: verilog\n")
|
|
56
|
+
f.write("input_paths:\n")
|
|
57
|
+
for node in self.get_files_from_input_nodes()[f"{self.design_topmodule}.lec.vg"]:
|
|
58
|
+
filename = self.compute_input_file_node_name(f"{self.design_topmodule}.lec.vg",
|
|
59
|
+
*node)
|
|
60
|
+
f.write(f" - inputs/{filename}\n")
|
|
61
|
+
f.write("liberty_files:\n")
|
|
62
|
+
|
|
63
|
+
scenario = list(self.project.constraint.timing.get_scenario().values())[0]
|
|
64
|
+
libcorners = scenario.get_libcorner(self.step, self.index)
|
|
65
|
+
delay_model = self.project.get("asic", "delaymodel")
|
|
66
|
+
for asiclib in self.project.get("asic", "asiclib"):
|
|
67
|
+
lib = self.project.get("library", asiclib, field="schema")
|
|
68
|
+
for corner in libcorners:
|
|
69
|
+
if not lib.valid("asic", "libcornerfileset", corner, delay_model):
|
|
70
|
+
continue
|
|
71
|
+
for fileset in lib.get("asic", "libcornerfileset", corner, delay_model):
|
|
72
|
+
for file in lib.get_file(fileset=fileset, filetype="liberty"):
|
|
73
|
+
f.write(f" - {file}\n")
|
|
74
|
+
f.write(f"log_file: {self.get_logpath('exe')}\n")
|
|
75
|
+
|
|
76
|
+
def runtime_options(self):
|
|
77
|
+
options = super().runtime_options()
|
|
78
|
+
options.append("--config")
|
|
79
|
+
options.append(self.__config_file())
|
|
80
|
+
return options
|
|
81
|
+
|
|
82
|
+
def post_process(self):
|
|
83
|
+
super().post_process()
|
|
84
|
+
# Kepler-formal writes its own log file; nothing to do here.
|
|
85
|
+
errors = 0
|
|
86
|
+
log = self.get_logpath('exe')
|
|
87
|
+
if os.path.exists(log):
|
|
88
|
+
with sc_open(log, 'r') as f:
|
|
89
|
+
for logline in f:
|
|
90
|
+
if "Found difference " in logline:
|
|
91
|
+
errors += 1
|
|
92
|
+
|
|
93
|
+
self.record_metric("drvs", errors, source_file=log)
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def make_docs(cls):
|
|
97
|
+
from siliconcompiler import Flowgraph, Design, ASIC
|
|
98
|
+
from siliconcompiler.scheduler import SchedulerNode
|
|
99
|
+
from siliconcompiler.targets import freepdk45_demo
|
|
100
|
+
design = Design("<design>")
|
|
101
|
+
with design.active_fileset("docs"):
|
|
102
|
+
design.set_topmodule("top")
|
|
103
|
+
proj = ASIC(design)
|
|
104
|
+
proj.add_fileset("docs")
|
|
105
|
+
freepdk45_demo(proj)
|
|
106
|
+
flow = Flowgraph("docsflow")
|
|
107
|
+
flow.node("<step>", cls(), index="<index>")
|
|
108
|
+
proj.set_flow(flow)
|
|
109
|
+
|
|
110
|
+
node = SchedulerNode(proj, "<step>", "<index>")
|
|
111
|
+
node.setup()
|
|
112
|
+
return node.task
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import Optional, Union
|
|
2
|
+
|
|
1
3
|
from siliconcompiler import ScreenshotTask, Task
|
|
2
4
|
from siliconcompiler.tools.klayout.show import ShowTask
|
|
3
5
|
|
|
@@ -20,7 +22,70 @@ class ScreenshotParams(Task):
|
|
|
20
22
|
self.add_parameter("show_linewidth", "int",
|
|
21
23
|
"Width of lines in detailed screenshots", defvalue=0, unit="px")
|
|
22
24
|
self.add_parameter("show_oversampling", "int",
|
|
23
|
-
"Image oversampling used in detailed screenshots
|
|
25
|
+
"Image oversampling used in detailed screenshots", defvalue=2)
|
|
26
|
+
|
|
27
|
+
def set_klayout_bins(self, xbins: int, ybins: int,
|
|
28
|
+
step: Optional[str] = None, index: Optional[Union[str, int]] = None):
|
|
29
|
+
"""
|
|
30
|
+
Set the number of bins for KLayout screenshotting.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
xbins (int): Number of bins along the x-axis.
|
|
34
|
+
ybins (int): Number of bins along the y-axis.
|
|
35
|
+
step (Optional[str]): Flow step to set the parameter for. Defaults to None.
|
|
36
|
+
index (Optional[Union[str, int]]): Index to set the parameter for. Defaults to None.
|
|
37
|
+
"""
|
|
38
|
+
self.set("var", "show_bins", (xbins, ybins), step=step, index=index)
|
|
39
|
+
|
|
40
|
+
def set_klayout_margin(self, margin: float,
|
|
41
|
+
step: Optional[str] = None, index: Optional[Union[str, int]] = None):
|
|
42
|
+
"""
|
|
43
|
+
Set the margin for KLayout screenshotting.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
margin (float): Margin around the design in microns.
|
|
47
|
+
step (Optional[str]): Flow step to set the parameter for. Defaults to None.
|
|
48
|
+
index (Optional[Union[str, int]]): Index to set the parameter for. Defaults to None.
|
|
49
|
+
"""
|
|
50
|
+
self.set("var", "show_margin", margin, step=step, index=index)
|
|
51
|
+
|
|
52
|
+
def set_klayout_resolution(self, xres: int, yres: int,
|
|
53
|
+
step: Optional[str] = None, index: Optional[Union[str, int]] = None):
|
|
54
|
+
"""
|
|
55
|
+
Set the resolution for KLayout screenshotting.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
xres (int): Horizontal resolution in pixels.
|
|
59
|
+
yres (int): Vertical resolution in pixels.
|
|
60
|
+
step (Optional[str]): Flow step to set the parameter for. Defaults to None.
|
|
61
|
+
index (Optional[Union[str, int]]): Index to set the parameter for. Defaults to None.
|
|
62
|
+
"""
|
|
63
|
+
self.set("var", "show_resolution", (xres, yres), step=step, index=index)
|
|
64
|
+
|
|
65
|
+
def set_klayout_linewidth(self, linewidth: int,
|
|
66
|
+
step: Optional[str] = None, index: Optional[Union[str, int]] = None):
|
|
67
|
+
"""
|
|
68
|
+
Set the linewidth for KLayout screenshotting.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
linewidth (int): Width of lines in detailed screenshots.
|
|
72
|
+
step (Optional[str]): Flow step to set the parameter for. Defaults to None.
|
|
73
|
+
index (Optional[Union[str, int]]): Index to set the parameter for. Defaults to None.
|
|
74
|
+
"""
|
|
75
|
+
self.set("var", "show_linewidth", linewidth, step=step, index=index)
|
|
76
|
+
|
|
77
|
+
def set_klayout_oversampling(self, oversampling: int,
|
|
78
|
+
step: Optional[str] = None,
|
|
79
|
+
index: Optional[Union[str, int]] = None):
|
|
80
|
+
"""
|
|
81
|
+
Set the oversampling for KLayout screenshotting.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
oversampling (int): Image oversampling used in detailed screenshots.
|
|
85
|
+
step (Optional[str]): Flow step to set the parameter for. Defaults to None.
|
|
86
|
+
index (Optional[Union[str, int]]): Index to set the parameter for. Defaults to None.
|
|
87
|
+
"""
|
|
88
|
+
self.set("var", "show_oversampling", oversampling, step=step, index=index)
|
|
24
89
|
|
|
25
90
|
def setup(self):
|
|
26
91
|
super().setup()
|
|
@@ -39,24 +39,23 @@ import sys
|
|
|
39
39
|
import fnmatch
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
def gds_export(design_name, in_def,
|
|
43
|
-
seal_file='',
|
|
44
|
-
timestamps=True):
|
|
42
|
+
def gds_export(design_name, in_def, out_file, tech, allow_missing, timestamps=True):
|
|
45
43
|
from klayout_utils import get_write_options # noqa E402
|
|
46
44
|
|
|
47
45
|
# Load def file
|
|
48
46
|
main_layout = pya.Layout()
|
|
49
47
|
main_layout.technology_name = tech.name
|
|
50
|
-
|
|
48
|
+
layout_options = tech.load_layout_options
|
|
49
|
+
main_layout.read(in_def, layout_options)
|
|
51
50
|
|
|
52
51
|
# List cells
|
|
53
52
|
def_cells = []
|
|
54
|
-
for
|
|
55
|
-
def_cells.append(
|
|
53
|
+
for cell_idx in main_layout.cell(design_name).each_child_cell():
|
|
54
|
+
def_cells.append(main_layout.cell_name(cell_idx))
|
|
56
55
|
|
|
57
|
-
def_cells.remove(design_name)
|
|
58
56
|
# Remove vias
|
|
59
|
-
|
|
57
|
+
via_prefix = layout_options.lefdef_config.via_cellname_prefix
|
|
58
|
+
def_cells = sorted([cell for cell in def_cells if not cell.startswith(via_prefix)])
|
|
60
59
|
print(f"[INFO] Read in {len(def_cells)} cells from DEF file")
|
|
61
60
|
for cell in def_cells:
|
|
62
61
|
print(f" [INFO] DEF cell: {cell}")
|
|
@@ -65,16 +64,11 @@ def gds_export(design_name, in_def, in_files, out_file, tech, allow_missing, con
|
|
|
65
64
|
def_cells.remove(f"{design_name}_DEF_FILL")
|
|
66
65
|
|
|
67
66
|
# Load in the gds to merge
|
|
68
|
-
|
|
69
|
-
for fil in in_files:
|
|
67
|
+
for fil in layout_options.lefdef_config.macro_layout_files:
|
|
70
68
|
macro_layout = pya.Layout()
|
|
71
69
|
macro_layout.read(fil)
|
|
72
|
-
print(f"[INFO] Read in {fil}")
|
|
73
70
|
for cell in list(def_cells):
|
|
74
71
|
if macro_layout.has_cell(cell):
|
|
75
|
-
subcell = main_layout.cell(cell)
|
|
76
|
-
print(f" [INFO] Merging in {cell}")
|
|
77
|
-
subcell.copy_tree(macro_layout.cell(cell))
|
|
78
72
|
def_cells.remove(cell)
|
|
79
73
|
|
|
80
74
|
# Copy the top level only to a new layout
|
|
@@ -100,18 +94,6 @@ def gds_export(design_name, in_def, in_files, out_file, tech, allow_missing, con
|
|
|
100
94
|
if i.name != design_name and i.parent_cells() == 0:
|
|
101
95
|
print("[ERROR] Found orphan cell '{0}'".format(i.name))
|
|
102
96
|
|
|
103
|
-
if seal_file:
|
|
104
|
-
top_cell = top_only_layout.top_cell()
|
|
105
|
-
|
|
106
|
-
print("[INFO] Reading seal GDS/OAS file...")
|
|
107
|
-
print("\t{0}".format(seal_file))
|
|
108
|
-
top_only_layout.read(seal_file)
|
|
109
|
-
|
|
110
|
-
for cell in top_only_layout.top_cells():
|
|
111
|
-
if cell != top_cell:
|
|
112
|
-
print("[INFO] Merging '{0}' as child of '{1}'".format(cell.name, top_cell.name))
|
|
113
|
-
top.insert(pya.CellInstArray(cell.cell_index(), pya.Trans()))
|
|
114
|
-
|
|
115
97
|
# Write out the GDS
|
|
116
98
|
print("[INFO] Writing out GDS/OAS '{0}'".format(out_file))
|
|
117
99
|
top_only_layout.write(out_file, get_write_options(out_file, timestamps))
|
|
@@ -125,7 +107,6 @@ def main():
|
|
|
125
107
|
|
|
126
108
|
from klayout_utils import (
|
|
127
109
|
technology,
|
|
128
|
-
get_streams,
|
|
129
110
|
save_technology,
|
|
130
111
|
get_schema,
|
|
131
112
|
generate_metrics
|
|
@@ -155,18 +136,8 @@ def main():
|
|
|
155
136
|
|
|
156
137
|
out_file = os.path.join('outputs', f'{design}.{sc_stream}')
|
|
157
138
|
|
|
158
|
-
in_files = []
|
|
159
|
-
libs = schema.get("asic", "asiclib")
|
|
160
|
-
for lib in libs:
|
|
161
|
-
libobj = schema.get("library", lib, field="schema")
|
|
162
|
-
for s in get_streams(schema):
|
|
163
|
-
for fileset in libobj.get("asic", "aprfileset"):
|
|
164
|
-
if libobj.valid("fileset", fileset, "file", s):
|
|
165
|
-
in_files.extend(libobj.get("fileset", fileset, "file", s))
|
|
166
|
-
break
|
|
167
|
-
|
|
168
139
|
allow_missing = []
|
|
169
|
-
for lib in
|
|
140
|
+
for lib in schema.get("asic", "asiclib"):
|
|
170
141
|
if schema.valid('library', lib, 'tool', 'klayout', 'allow_missing_cell'):
|
|
171
142
|
patterns = [pattern for pattern in schema.get('library', lib, 'tool', 'klayout',
|
|
172
143
|
'allow_missing_cell') if pattern]
|
|
@@ -179,8 +150,7 @@ def main():
|
|
|
179
150
|
|
|
180
151
|
sc_tech = technology(design, schema)
|
|
181
152
|
|
|
182
|
-
gds_export(design, in_def,
|
|
183
|
-
config_file='', seal_file='', timestamps=sc_timestamps)
|
|
153
|
+
gds_export(design, in_def, out_file, sc_tech, allow_missing, timestamps=sc_timestamps)
|
|
184
154
|
|
|
185
155
|
if sc_screenshot:
|
|
186
156
|
show(schema, sc_tech, out_file, f'outputs/{design}.png', screenshot=True)
|
|
@@ -25,7 +25,7 @@ def show(schema, tech, input_path, output_path, screenshot=False, report=None):
|
|
|
25
25
|
layout_options.lefdef_config.produce_obstructions = True
|
|
26
26
|
|
|
27
27
|
# Always use LEF geometry even when LEF file contains FOREIGN statement.
|
|
28
|
-
layout_options.lefdef_config.macro_resolution_mode =
|
|
28
|
+
layout_options.lefdef_config.macro_resolution_mode = 0
|
|
29
29
|
|
|
30
30
|
tech.load_layout_options = layout_options
|
|
31
31
|
|
|
@@ -118,9 +118,9 @@ def __screenshot_montage(schema, view, xbins, ybins):
|
|
|
118
118
|
|
|
119
119
|
app.set_config("background-color", "#000000") # Black
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
design_name = schema.get('option', 'design')
|
|
122
|
+
fileset = schema.get("option", "fileset")[0]
|
|
123
|
+
design = schema.get("library", design_name, "fileset", fileset, "topmodule")
|
|
124
124
|
|
|
125
125
|
horizontal_resolution, vertical_resolution = schema.get(
|
|
126
126
|
'tool', 'klayout', 'task', task, 'var', 'show_resolution', step=step, index=index)
|
|
@@ -76,7 +76,7 @@ def technology(design, schema):
|
|
|
76
76
|
|
|
77
77
|
layout_options = tech.load_layout_options
|
|
78
78
|
|
|
79
|
-
layout_options.lefdef_config.macro_resolution_mode =
|
|
79
|
+
layout_options.lefdef_config.macro_resolution_mode = 0
|
|
80
80
|
layout_options.lefdef_config.via_cellname_prefix = "VIA_"
|
|
81
81
|
pathed_files = set()
|
|
82
82
|
for lef_file in layout_options.lefdef_config.lef_files:
|
|
@@ -98,6 +98,18 @@ def technology(design, schema):
|
|
|
98
98
|
for lef_file in layout_options.lefdef_config.lef_files:
|
|
99
99
|
print(f"[INFO] LEF file: {lef_file}")
|
|
100
100
|
|
|
101
|
+
in_files = layout_options.lefdef_config.macro_layout_files
|
|
102
|
+
for lib in schema.get("asic", "asiclib"):
|
|
103
|
+
libobj = schema.get("library", lib, field="schema")
|
|
104
|
+
for s in get_streams(schema):
|
|
105
|
+
for fileset in libobj.get("asic", "aprfileset"):
|
|
106
|
+
if libobj.valid("fileset", fileset, "file", s):
|
|
107
|
+
in_files.extend(libobj.get("fileset", fileset, "file", s))
|
|
108
|
+
break
|
|
109
|
+
layout_options.lefdef_config.macro_layout_files = in_files
|
|
110
|
+
for lef_file in layout_options.lefdef_config.macro_layout_files:
|
|
111
|
+
print(f"[INFO] Layout file: {lef_file}")
|
|
112
|
+
|
|
101
113
|
# Set layer properties
|
|
102
114
|
layer_properties = tech.layer_properties_file
|
|
103
115
|
if layer_properties:
|
|
@@ -1,20 +1,11 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Optional, Union
|
|
2
|
+
|
|
3
|
+
from siliconcompiler import Task, TaskSkip
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
class TileTask(Task):
|
|
5
7
|
'''
|
|
6
8
|
Tiles input images into a single output image.
|
|
7
|
-
|
|
8
|
-
Notes:
|
|
9
|
-
Need to make ensure that /etc/ImageMagick-6/policy.xml
|
|
10
|
-
<policy domain="resource" name="memory" value="8GiB"/>
|
|
11
|
-
<policy domain="resource" name="map" value="8GiB"/>
|
|
12
|
-
<policy domain="resource" name="width" value="32KP"/>
|
|
13
|
-
<policy domain="resource" name="height" value="32KP"/>
|
|
14
|
-
<policy domain="resource" name="area" value="1GP"/>
|
|
15
|
-
<policy domain="resource" name="disk" value="8GiB"/>
|
|
16
|
-
This ensures there are enough resources available to generate
|
|
17
|
-
the final image.
|
|
18
9
|
'''
|
|
19
10
|
|
|
20
11
|
def __init__(self):
|
|
@@ -23,6 +14,19 @@ class TileTask(Task):
|
|
|
23
14
|
self.add_parameter("bins", "(int,int)", "Number of bins along the (x, y)-axis",
|
|
24
15
|
defvalue=(2, 2))
|
|
25
16
|
|
|
17
|
+
def set_montage_bins(self, xbins: int, ybins: int,
|
|
18
|
+
step: Optional[str] = None, index: Optional[Union[str, int]] = None):
|
|
19
|
+
"""
|
|
20
|
+
Set the number of bins for Montage tiling.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
xbins (int): Number of bins along the x-axis.
|
|
24
|
+
ybins (int): Number of bins along the y-axis.
|
|
25
|
+
step (Optional[str]): Flow step to set the parameter for. Defaults to None.
|
|
26
|
+
index (Optional[Union[str, int]]): Index to set the parameter for. Defaults to None.
|
|
27
|
+
"""
|
|
28
|
+
self.set("var", "bins", (xbins, ybins), step=step, index=index)
|
|
29
|
+
|
|
26
30
|
def tool(self):
|
|
27
31
|
return "montage"
|
|
28
32
|
|
|
@@ -41,12 +45,22 @@ class TileTask(Task):
|
|
|
41
45
|
|
|
42
46
|
xbins, ybins = self.get("var", "bins")
|
|
43
47
|
|
|
48
|
+
if f"{self.design_topmodule}.png" in self.get_files_from_input_nodes():
|
|
49
|
+
raise TaskSkip("Input provides a single image; skipping tiling.")
|
|
50
|
+
|
|
44
51
|
for x in range(xbins):
|
|
45
52
|
for y in range(ybins):
|
|
46
53
|
self.add_input_file(f'{self.design_topmodule}_X{x}_Y{y}.png')
|
|
47
54
|
|
|
48
55
|
self.add_output_file(ext="png")
|
|
49
56
|
|
|
57
|
+
self.add_commandline_option(['-limit', 'memory', '8GiB'])
|
|
58
|
+
self.add_commandline_option(['-limit', 'map', '8GiB'])
|
|
59
|
+
self.add_commandline_option(['-limit', 'disk', '8GiB'])
|
|
60
|
+
self.add_commandline_option(['-limit', 'width', '32KP'])
|
|
61
|
+
self.add_commandline_option(['-limit', 'height', '32KP'])
|
|
62
|
+
self.add_commandline_option(['-limit', 'area', '1GP'])
|
|
63
|
+
|
|
50
64
|
def runtime_options(self):
|
|
51
65
|
options = super().runtime_options()
|
|
52
66
|
|
|
@@ -374,9 +374,12 @@ class OpenROADDRTParameter(_OpenROADDRTCommonParameter):
|
|
|
374
374
|
self.add_parameter("drt_disable_via_gen", "bool",
|
|
375
375
|
"true/false, when true turns off via generation in detailed router "
|
|
376
376
|
"and only uses the specified tech vias", defvalue=False)
|
|
377
|
-
self.add_parameter("drt_via_in_pin_bottom_layer", "str",
|
|
378
|
-
|
|
379
|
-
self.add_parameter("
|
|
377
|
+
self.add_parameter("drt_via_in_pin_bottom_layer", "str",
|
|
378
|
+
"bottom layer to allow vias inside pins")
|
|
379
|
+
self.add_parameter("drt_via_in_pin_top_layer", "str",
|
|
380
|
+
"top layer to allow vias inside pins")
|
|
381
|
+
self.add_parameter("drt_repair_pdn_vias", "str",
|
|
382
|
+
"layer to repair PDN vias on")
|
|
380
383
|
|
|
381
384
|
self.add_parameter("drt_report_interval", "int",
|
|
382
385
|
"reporting interval in steps for generating a DRC report.", defvalue=5)
|
|
@@ -635,13 +638,22 @@ class APRTask(OpenROADTask):
|
|
|
635
638
|
def _add_pnr_outputs(self):
|
|
636
639
|
self.add_output_file(ext="sdc")
|
|
637
640
|
self.add_output_file(ext="vg")
|
|
641
|
+
self.add_output_file(ext="lec.vg")
|
|
638
642
|
self.add_output_file(ext="def")
|
|
639
643
|
self.add_output_file(ext="odb")
|
|
640
644
|
|
|
645
|
+
for lib in self.project.get("asic", "asiclib"):
|
|
646
|
+
libobj = self.project.get("library", lib, field="schema")
|
|
647
|
+
for celltype in ["decap", "tie", "filler", "tap", "endcap", "antenna", "physicalonly"]:
|
|
648
|
+
if libobj.valid("asic", "cells", celltype) and \
|
|
649
|
+
libobj.get("asic", "cells", celltype):
|
|
650
|
+
self.add_required_key(libobj, "asic", "cells", celltype)
|
|
651
|
+
|
|
641
652
|
def _get_pex_mapping(self):
|
|
642
653
|
corners = {}
|
|
643
|
-
for constraint in self.project.getkeys('constraint', 'timing'):
|
|
644
|
-
pexcorner = self.project.get('constraint', 'timing',
|
|
654
|
+
for constraint in self.project.getkeys('constraint', 'timing', 'scenario'):
|
|
655
|
+
pexcorner = self.project.get('constraint', 'timing', 'scenario',
|
|
656
|
+
constraint, 'pexcorner',
|
|
645
657
|
step=self.step, index=self.index)
|
|
646
658
|
if pexcorner:
|
|
647
659
|
corners[constraint] = pexcorner
|
|
@@ -649,13 +661,17 @@ class APRTask(OpenROADTask):
|
|
|
649
661
|
return corners
|
|
650
662
|
|
|
651
663
|
def _get_constraint_by_check(self, check: str) -> str:
|
|
652
|
-
for constraint in self.project.getkeys('constraint', 'timing'):
|
|
653
|
-
if check in self.project.get('constraint', 'timing',
|
|
664
|
+
for constraint in self.project.getkeys('constraint', 'timing', 'scenario'):
|
|
665
|
+
if check in self.project.get('constraint', 'timing', 'scenario',
|
|
666
|
+
constraint, 'check',
|
|
654
667
|
step=self.step, index=self.index):
|
|
655
668
|
return constraint
|
|
656
669
|
|
|
657
670
|
# if not specified, just pick the first constraint available
|
|
658
|
-
|
|
671
|
+
scenarios = self.project.getkeys('constraint', 'timing', 'scenario')
|
|
672
|
+
if not scenarios:
|
|
673
|
+
raise ValueError("No timing scenarios defined in project constraints.")
|
|
674
|
+
return scenarios[0]
|
|
659
675
|
|
|
660
676
|
def _build_pex_estimation_file(self):
|
|
661
677
|
corners = self._get_pex_mapping()
|
|
@@ -761,9 +777,9 @@ class APRTask(OpenROADTask):
|
|
|
761
777
|
],
|
|
762
778
|
"peakpower": [
|
|
763
779
|
*[f"power/{corner}.rpt"
|
|
764
|
-
for corner in self.project.getkeys('constraint', 'timing')],
|
|
780
|
+
for corner in self.project.getkeys('constraint', 'timing', 'scenario')],
|
|
765
781
|
*[f"images/heatmap/power_density/{corner}.png"
|
|
766
|
-
for corner in self.project.getkeys('constraint', 'timing')]
|
|
782
|
+
for corner in self.project.getkeys('constraint', 'timing', 'scenario')]
|
|
767
783
|
],
|
|
768
784
|
"drvs": [
|
|
769
785
|
"timing/drv_violators.rpt",
|
|
@@ -149,8 +149,11 @@ class InitFloorplanTask(APRTask,
|
|
|
149
149
|
|
|
150
150
|
# Mark requires for components, pin, and floorplan placements
|
|
151
151
|
for component in self.project.constraint.component.get_component().values():
|
|
152
|
-
self.
|
|
153
|
-
|
|
152
|
+
if component.get_placement(step=self.step, index=self.index) is not None:
|
|
153
|
+
self.add_required_key(component, "placement")
|
|
154
|
+
self.add_required_key(component, "rotation")
|
|
155
|
+
if component.get_halo(step=self.step, index=self.index) is not None:
|
|
156
|
+
self.add_required_key(component, "halo")
|
|
154
157
|
if component.get_partname(step=self.step, index=self.index):
|
|
155
158
|
self.add_required_key(component, "partname")
|
|
156
159
|
|
|
@@ -142,7 +142,7 @@ class PowerGridAnalysisTask(APRTask, OpenROADPSMParameter, OpenROADSTAParameter)
|
|
|
142
142
|
super().setup()
|
|
143
143
|
self.set_script("apr/sc_irdrop.tcl")
|
|
144
144
|
|
|
145
|
-
self.add_version(">=v2.0-
|
|
145
|
+
self.add_version(">=v2.0-26866", clobber=True)
|
|
146
146
|
|
|
147
147
|
# Output is not a standard design file, unset default expectation
|
|
148
148
|
self.unset("output")
|
|
@@ -299,6 +299,8 @@ if { [sc_cfg_exists constraint component] } {
|
|
|
299
299
|
set x_grid [ord::dbu_to_microns $x_grid]
|
|
300
300
|
set y_grid [ord::dbu_to_microns $y_grid]
|
|
301
301
|
|
|
302
|
+
set sc_placed_insts []
|
|
303
|
+
|
|
302
304
|
dict for {name params} [sc_cfg_get constraint component] {
|
|
303
305
|
set location [dict get $params placement]
|
|
304
306
|
set rotation [sc_convert_rotation [dict get $params rotation]]
|
|
@@ -308,11 +310,27 @@ if { [sc_cfg_exists constraint component] } {
|
|
|
308
310
|
} else {
|
|
309
311
|
set cell ""
|
|
310
312
|
}
|
|
313
|
+
set halo {}
|
|
311
314
|
if { [llength [dict get $params halo]] != 0 } {
|
|
312
|
-
|
|
315
|
+
if { [llength [dict get $params halo]] == 2 } {
|
|
316
|
+
set halo [dict get $params halo]
|
|
317
|
+
} else {
|
|
318
|
+
utl::warn FLW 1 "Halo must be a list of 2 elements"
|
|
319
|
+
}
|
|
313
320
|
}
|
|
314
321
|
|
|
315
|
-
set
|
|
322
|
+
set stainst [get_cells -quiet $name]
|
|
323
|
+
if { $stainst != {} } {
|
|
324
|
+
if { [llength $stainst] > 1 } {
|
|
325
|
+
utl::error FLW 1 "Multiple cells found for instance $name"
|
|
326
|
+
}
|
|
327
|
+
set inst [sta::sta_to_db_inst $stainst]
|
|
328
|
+
} else {
|
|
329
|
+
set inst "NULL"
|
|
330
|
+
}
|
|
331
|
+
if { $inst == "NULL" } {
|
|
332
|
+
set inst [[ord::get_db_block] findInst $name]
|
|
333
|
+
}
|
|
316
334
|
if { $inst == "NULL" } {
|
|
317
335
|
utl::warn FLW 1 "Could not find instance: $name"
|
|
318
336
|
|
|
@@ -324,23 +342,40 @@ if { [sc_cfg_exists constraint component] } {
|
|
|
324
342
|
set cell ""
|
|
325
343
|
}
|
|
326
344
|
|
|
327
|
-
|
|
328
|
-
|
|
345
|
+
if { $inst != "NULL" } {
|
|
346
|
+
set name [$inst getName]
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if { [llength $location] == 2 } {
|
|
350
|
+
# Only place if location is specified
|
|
351
|
+
set x_loc [expr { round([lindex $location 0] / $x_grid) * $x_grid }]
|
|
352
|
+
set y_loc [expr { round([lindex $location 1] / $y_grid) * $y_grid }]
|
|
353
|
+
|
|
354
|
+
set place_inst_args []
|
|
355
|
+
if { $cell != "" } {
|
|
356
|
+
lappend place_inst_args -cell $cell
|
|
357
|
+
}
|
|
329
358
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
359
|
+
place_inst \
|
|
360
|
+
-name $name \
|
|
361
|
+
-origin "$x_loc $y_loc" \
|
|
362
|
+
-orient $rotation \
|
|
363
|
+
-status FIRM \
|
|
364
|
+
{*}$place_inst_args
|
|
333
365
|
}
|
|
334
366
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
367
|
+
if { $halo != {} } {
|
|
368
|
+
set inst [[ord::get_db_block] findInst $name]
|
|
369
|
+
odb::dbBox_create $inst \
|
|
370
|
+
[ord::microns_to_dbu [lindex $halo 0]] \
|
|
371
|
+
[ord::microns_to_dbu [lindex $halo 1]] \
|
|
372
|
+
[ord::microns_to_dbu [lindex $halo 0]] \
|
|
373
|
+
[ord::microns_to_dbu [lindex $halo 1]]
|
|
374
|
+
}
|
|
375
|
+
lappend sc_placed_insts $name
|
|
341
376
|
}
|
|
342
377
|
|
|
343
|
-
sc_print_macro_information
|
|
378
|
+
sc_print_macro_information $sc_placed_insts
|
|
344
379
|
}
|
|
345
380
|
|
|
346
381
|
if { [sc_check_version 23008] } {
|
|
@@ -89,7 +89,8 @@ foreach net $nets {
|
|
|
89
89
|
foreach corner $sc_scenarios {
|
|
90
90
|
analyze_power_grid -net $net -corner $corner -allow_reuse
|
|
91
91
|
|
|
92
|
-
save_animated_gif -start "reports/${net}/${corner}.gif"
|
|
92
|
+
set gif [save_animated_gif -start "reports/${net}/${corner}.gif"]
|
|
93
|
+
set gif_log [save_animated_gif -start "reports/${net}/${corner}_log.gif"]
|
|
93
94
|
|
|
94
95
|
foreach layer [[ord::get_db_tech] getLayers] {
|
|
95
96
|
if { [$layer getRoutingLevel] == 0 } {
|
|
@@ -123,7 +124,7 @@ foreach net $nets {
|
|
|
123
124
|
sc_save_image \
|
|
124
125
|
"IR drop for $net on $layer_name for $corner heatmap" \
|
|
125
126
|
reports/${net}/${corner}.${layer_name}.png \
|
|
126
|
-
|
|
127
|
+
$gif
|
|
127
128
|
|
|
128
129
|
gui::set_heatmap IRDrop LogScale 1
|
|
129
130
|
gui::set_heatmap IRDrop rebuild
|
|
@@ -131,7 +132,7 @@ foreach net $nets {
|
|
|
131
132
|
sc_save_image \
|
|
132
133
|
"IR drop for $net on $layer_name for $corner heatmap" \
|
|
133
134
|
reports/${net}/${corner}.${layer_name}_log.png \
|
|
134
|
-
|
|
135
|
+
$gif_log
|
|
135
136
|
|
|
136
137
|
gui::set_display_controls "Heat Maps/IR Drop" visible false
|
|
137
138
|
|
|
@@ -139,7 +140,8 @@ foreach net $nets {
|
|
|
139
140
|
gui::delete_label $label
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
|
-
save_animated_gif -end
|
|
143
|
+
save_animated_gif -end -key $gif
|
|
144
|
+
save_animated_gif -end -key $gif_log
|
|
143
145
|
}
|
|
144
146
|
}
|
|
145
147
|
|