librelane 2.4.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.
Potentially problematic release.
This version of librelane might be problematic. Click here for more details.
- librelane/__init__.py +38 -0
- librelane/__main__.py +479 -0
- librelane/__version__.py +43 -0
- librelane/common/__init__.py +63 -0
- librelane/common/cli.py +75 -0
- librelane/common/drc.py +246 -0
- librelane/common/generic_dict.py +319 -0
- librelane/common/metrics/__init__.py +35 -0
- librelane/common/metrics/__main__.py +413 -0
- librelane/common/metrics/library.py +354 -0
- librelane/common/metrics/metric.py +186 -0
- librelane/common/metrics/util.py +279 -0
- librelane/common/misc.py +456 -0
- librelane/common/ring_buffer.py +63 -0
- librelane/common/tcl.py +80 -0
- librelane/common/toolbox.py +549 -0
- librelane/common/tpe.py +41 -0
- librelane/common/types.py +116 -0
- librelane/config/__init__.py +32 -0
- librelane/config/__main__.py +155 -0
- librelane/config/config.py +1025 -0
- librelane/config/flow.py +490 -0
- librelane/config/pdk_compat.py +255 -0
- librelane/config/preprocessor.py +464 -0
- librelane/config/removals.py +45 -0
- librelane/config/variable.py +743 -0
- librelane/container.py +285 -0
- librelane/env_info.py +320 -0
- librelane/examples/spm/config.yaml +33 -0
- librelane/examples/spm/pin_order.cfg +14 -0
- librelane/examples/spm/src/impl.sdc +73 -0
- librelane/examples/spm/src/signoff.sdc +68 -0
- librelane/examples/spm/src/spm.v +73 -0
- librelane/examples/spm/verify/spm_tb.v +106 -0
- librelane/examples/spm-user_project_wrapper/SPM_example.v +286 -0
- librelane/examples/spm-user_project_wrapper/base_sdc_file.sdc +145 -0
- librelane/examples/spm-user_project_wrapper/config-tut.json +12 -0
- librelane/examples/spm-user_project_wrapper/config.json +13 -0
- librelane/examples/spm-user_project_wrapper/defines.v +66 -0
- librelane/examples/spm-user_project_wrapper/template.def +7656 -0
- librelane/examples/spm-user_project_wrapper/user_project_wrapper.v +123 -0
- librelane/flows/__init__.py +24 -0
- librelane/flows/builtins.py +18 -0
- librelane/flows/classic.py +327 -0
- librelane/flows/cli.py +463 -0
- librelane/flows/flow.py +1049 -0
- librelane/flows/misc.py +71 -0
- librelane/flows/optimizing.py +179 -0
- librelane/flows/sequential.py +367 -0
- librelane/flows/synth_explore.py +173 -0
- librelane/help/__main__.py +39 -0
- librelane/logging/__init__.py +40 -0
- librelane/logging/logger.py +323 -0
- librelane/open_pdks_rev +1 -0
- librelane/plugins.py +21 -0
- librelane/py.typed +0 -0
- librelane/scripts/base.sdc +80 -0
- librelane/scripts/klayout/Readme.md +2 -0
- librelane/scripts/klayout/open_design.py +63 -0
- librelane/scripts/klayout/render.py +121 -0
- librelane/scripts/klayout/stream_out.py +176 -0
- librelane/scripts/klayout/xml_drc_report_to_json.py +45 -0
- librelane/scripts/klayout/xor.drc +120 -0
- librelane/scripts/magic/Readme.md +1 -0
- librelane/scripts/magic/common/read.tcl +114 -0
- librelane/scripts/magic/def/antenna_check.tcl +35 -0
- librelane/scripts/magic/def/mag.tcl +19 -0
- librelane/scripts/magic/def/mag_gds.tcl +79 -0
- librelane/scripts/magic/drc.tcl +78 -0
- librelane/scripts/magic/extract_spice.tcl +98 -0
- librelane/scripts/magic/gds/drc_batch.tcl +74 -0
- librelane/scripts/magic/gds/erase_box.tcl +32 -0
- librelane/scripts/magic/gds/extras_mag.tcl +45 -0
- librelane/scripts/magic/gds/mag_with_pointers.tcl +31 -0
- librelane/scripts/magic/get_bbox.tcl +11 -0
- librelane/scripts/magic/lef/extras_maglef.tcl +61 -0
- librelane/scripts/magic/lef/maglef.tcl +26 -0
- librelane/scripts/magic/lef.tcl +57 -0
- librelane/scripts/magic/open.tcl +28 -0
- librelane/scripts/magic/wrapper.tcl +21 -0
- librelane/scripts/netgen/setup.tcl +28 -0
- librelane/scripts/odbpy/apply_def_template.py +49 -0
- librelane/scripts/odbpy/cell_frequency.py +107 -0
- librelane/scripts/odbpy/check_antenna_properties.py +116 -0
- librelane/scripts/odbpy/contextualize.py +109 -0
- librelane/scripts/odbpy/defutil.py +573 -0
- librelane/scripts/odbpy/diodes.py +373 -0
- librelane/scripts/odbpy/disconnected_pins.py +305 -0
- librelane/scripts/odbpy/eco_buffer.py +181 -0
- librelane/scripts/odbpy/eco_diode.py +139 -0
- librelane/scripts/odbpy/filter_unannotated.py +100 -0
- librelane/scripts/odbpy/io_place.py +482 -0
- librelane/scripts/odbpy/ioplace_parser/__init__.py +23 -0
- librelane/scripts/odbpy/ioplace_parser/parse.py +147 -0
- librelane/scripts/odbpy/label_macro_pins.py +277 -0
- librelane/scripts/odbpy/lefutil.py +97 -0
- librelane/scripts/odbpy/placers.py +162 -0
- librelane/scripts/odbpy/power_utils.py +397 -0
- librelane/scripts/odbpy/random_place.py +57 -0
- librelane/scripts/odbpy/reader.py +250 -0
- librelane/scripts/odbpy/remove_buffers.py +173 -0
- librelane/scripts/odbpy/snap_to_grid.py +57 -0
- librelane/scripts/odbpy/wire_lengths.py +93 -0
- librelane/scripts/openroad/antenna_check.tcl +20 -0
- librelane/scripts/openroad/antenna_repair.tcl +31 -0
- librelane/scripts/openroad/basic_mp.tcl +24 -0
- librelane/scripts/openroad/buffer_list.tcl +10 -0
- librelane/scripts/openroad/common/dpl.tcl +24 -0
- librelane/scripts/openroad/common/dpl_cell_pad.tcl +26 -0
- librelane/scripts/openroad/common/grt.tcl +32 -0
- librelane/scripts/openroad/common/io.tcl +540 -0
- librelane/scripts/openroad/common/pdn_cfg.tcl +135 -0
- librelane/scripts/openroad/common/resizer.tcl +103 -0
- librelane/scripts/openroad/common/set_global_connections.tcl +78 -0
- librelane/scripts/openroad/common/set_layer_adjustments.tcl +31 -0
- librelane/scripts/openroad/common/set_power_nets.tcl +30 -0
- librelane/scripts/openroad/common/set_rc.tcl +75 -0
- librelane/scripts/openroad/common/set_routing_layers.tcl +30 -0
- librelane/scripts/openroad/cts.tcl +80 -0
- librelane/scripts/openroad/cut_rows.tcl +24 -0
- librelane/scripts/openroad/dpl.tcl +24 -0
- librelane/scripts/openroad/drt.tcl +37 -0
- librelane/scripts/openroad/fill.tcl +30 -0
- librelane/scripts/openroad/floorplan.tcl +145 -0
- librelane/scripts/openroad/gpl.tcl +88 -0
- librelane/scripts/openroad/grt.tcl +30 -0
- librelane/scripts/openroad/gui.tcl +37 -0
- librelane/scripts/openroad/insert_buffer.tcl +127 -0
- librelane/scripts/openroad/ioplacer.tcl +67 -0
- librelane/scripts/openroad/irdrop.tcl +51 -0
- librelane/scripts/openroad/pdn.tcl +52 -0
- librelane/scripts/openroad/rcx.tcl +32 -0
- librelane/scripts/openroad/repair_design.tcl +70 -0
- librelane/scripts/openroad/repair_design_postgrt.tcl +48 -0
- librelane/scripts/openroad/rsz_timing_postcts.tcl +68 -0
- librelane/scripts/openroad/rsz_timing_postgrt.tcl +70 -0
- librelane/scripts/openroad/sta/check_macro_instances.tcl +53 -0
- librelane/scripts/openroad/sta/corner.tcl +393 -0
- librelane/scripts/openroad/tapcell.tcl +25 -0
- librelane/scripts/openroad/write_views.tcl +27 -0
- librelane/scripts/pyosys/construct_abc_script.py +177 -0
- librelane/scripts/pyosys/json_header.py +84 -0
- librelane/scripts/pyosys/synthesize.py +493 -0
- librelane/scripts/pyosys/ys_common.py +153 -0
- librelane/scripts/tclsh/hello.tcl +1 -0
- librelane/state/__init__.py +24 -0
- librelane/state/__main__.py +61 -0
- librelane/state/design_format.py +195 -0
- librelane/state/state.py +359 -0
- librelane/steps/__init__.py +61 -0
- librelane/steps/__main__.py +510 -0
- librelane/steps/checker.py +637 -0
- librelane/steps/common_variables.py +340 -0
- librelane/steps/cvc_rv.py +169 -0
- librelane/steps/klayout.py +509 -0
- librelane/steps/magic.py +576 -0
- librelane/steps/misc.py +160 -0
- librelane/steps/netgen.py +253 -0
- librelane/steps/odb.py +1088 -0
- librelane/steps/openroad.py +2460 -0
- librelane/steps/openroad_alerts.py +102 -0
- librelane/steps/pyosys.py +640 -0
- librelane/steps/step.py +1571 -0
- librelane/steps/tclstep.py +288 -0
- librelane/steps/verilator.py +222 -0
- librelane/steps/yosys.py +371 -0
- librelane-2.4.0.dist-info/METADATA +169 -0
- librelane-2.4.0.dist-info/RECORD +170 -0
- librelane-2.4.0.dist-info/WHEEL +4 -0
- librelane-2.4.0.dist-info/entry_points.txt +9 -0
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
# Copyright 2023 Efabless Corporation
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
import re
|
|
17
|
+
from typing import ClassVar, Set, Tuple, List
|
|
18
|
+
from decimal import Decimal
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
from .step import ViewsUpdate, MetricsUpdate, Step, StepError, DeferredStepError, State
|
|
22
|
+
|
|
23
|
+
from ..logging import info, debug, verbose
|
|
24
|
+
from ..config import Variable
|
|
25
|
+
from ..common import Filter, parse_metric_modifiers
|
|
26
|
+
from ..state import DesignFormat
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@Step.factory.register()
|
|
30
|
+
class NetlistAssignStatements(Step):
|
|
31
|
+
"""
|
|
32
|
+
Raises a StepError if the Netlist has an ``assign`` statement in it.
|
|
33
|
+
|
|
34
|
+
``assign`` statements are known to cause bugs in some PnR tools.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
id = "Checker.NetlistAssignStatements"
|
|
38
|
+
name = "Netlist Assign Statement Checker"
|
|
39
|
+
|
|
40
|
+
inputs = [DesignFormat.NETLIST]
|
|
41
|
+
outputs = []
|
|
42
|
+
|
|
43
|
+
config_vars = [
|
|
44
|
+
Variable(
|
|
45
|
+
"ERROR_ON_NL_ASSIGN_STATEMENTS",
|
|
46
|
+
bool,
|
|
47
|
+
"Whether to emit an error or simply warn about the existence",
|
|
48
|
+
default=True,
|
|
49
|
+
)
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
|
|
53
|
+
assign_rx = re.compile(r"^\s*\bassign\b")
|
|
54
|
+
netlist_in = str(state_in[DesignFormat.NETLIST])
|
|
55
|
+
emit_error = self.config["ERROR_ON_NL_ASSIGN_STATEMENTS"]
|
|
56
|
+
found = False
|
|
57
|
+
with open(netlist_in, "r", encoding="utf8") as f:
|
|
58
|
+
for i, line in enumerate(f, start=1):
|
|
59
|
+
if assign_rx.search(line) is not None:
|
|
60
|
+
found = True
|
|
61
|
+
(self.err if emit_error else self.warn)(
|
|
62
|
+
f"{os.path.relpath(netlist_in)}:{i}: assign statement found in netlist"
|
|
63
|
+
)
|
|
64
|
+
if found and self.config["ERROR_ON_NL_ASSIGN_STATEMENTS"]:
|
|
65
|
+
raise StepError("One or more assign statements found in the netlist.")
|
|
66
|
+
return {}, {}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class MetricChecker(Step):
|
|
70
|
+
"""
|
|
71
|
+
Raises a (deferred) error if a Decimal metric exceeds a certain threshold.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
inputs = []
|
|
75
|
+
outputs = []
|
|
76
|
+
|
|
77
|
+
metric_name: ClassVar[str] = NotImplemented
|
|
78
|
+
metric_description: ClassVar[str] = NotImplemented
|
|
79
|
+
deferred: ClassVar[bool] = True
|
|
80
|
+
error_on_var: Optional[Variable] = None
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def get_help_md(Self, **kwargs): # pragma: no cover
|
|
84
|
+
threshold_string = Self.get_threshold_description(None)
|
|
85
|
+
if threshold_string is None:
|
|
86
|
+
threshold_string = str(Self.get_threshold(None))
|
|
87
|
+
dynamic_docstring = "Raises"
|
|
88
|
+
if Self.deferred:
|
|
89
|
+
dynamic_docstring += " a deferred error"
|
|
90
|
+
else:
|
|
91
|
+
dynamic_docstring += " an immediate error"
|
|
92
|
+
dynamic_docstring += f" if {Self.metric_description} (metric: ``{Self.metric_name}``) are >= {threshold_string}."
|
|
93
|
+
dynamic_docstring += (
|
|
94
|
+
" Doesn't raise an error depending on error_on_var if defined."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return super().get_help_md(docstring_override=dynamic_docstring, **kwargs)
|
|
98
|
+
|
|
99
|
+
def get_threshold(self: Optional["MetricChecker"]) -> Optional[Decimal]:
|
|
100
|
+
return Decimal(0)
|
|
101
|
+
|
|
102
|
+
def get_threshold_description(self: Optional["MetricChecker"]) -> Optional[str]:
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
|
|
106
|
+
threshold = self.get_threshold()
|
|
107
|
+
|
|
108
|
+
if threshold is None:
|
|
109
|
+
self.warn(
|
|
110
|
+
f"Threshold for {self.metric_description} is not set. The checker will be skipped."
|
|
111
|
+
)
|
|
112
|
+
else:
|
|
113
|
+
metric_value = state_in.metrics.get(self.metric_name)
|
|
114
|
+
if metric_value is not None:
|
|
115
|
+
if metric_value > threshold:
|
|
116
|
+
error_msg = f"{metric_value} {self.metric_description} found."
|
|
117
|
+
if (
|
|
118
|
+
hasattr(self, "error_on_var")
|
|
119
|
+
and self.error_on_var
|
|
120
|
+
and not self.config.get(self.error_on_var.name)
|
|
121
|
+
):
|
|
122
|
+
debug(self.config.get(self.error_on_var.name))
|
|
123
|
+
self.warn(f"{error_msg}")
|
|
124
|
+
elif self.deferred:
|
|
125
|
+
self.err(f"{error_msg} - deferred")
|
|
126
|
+
raise DeferredStepError(error_msg)
|
|
127
|
+
else:
|
|
128
|
+
self.err(f"{error_msg}")
|
|
129
|
+
raise StepError(error_msg)
|
|
130
|
+
|
|
131
|
+
else:
|
|
132
|
+
info(f"Check for {self.metric_description} clear.")
|
|
133
|
+
else:
|
|
134
|
+
self.warn(
|
|
135
|
+
f"The {self.metric_description} metric was not found. Are you sure the relevant step was run?"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return {}, {}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@Step.factory.register()
|
|
142
|
+
class YosysUnmappedCells(MetricChecker):
|
|
143
|
+
id = "Checker.YosysUnmappedCells"
|
|
144
|
+
name = "Unmapped Cells Checker"
|
|
145
|
+
deferred = False
|
|
146
|
+
|
|
147
|
+
metric_name = "design__instance_unmapped__count"
|
|
148
|
+
metric_description = "Unmapped Yosys instances"
|
|
149
|
+
|
|
150
|
+
error_on_var = Variable(
|
|
151
|
+
"ERROR_ON_UNMAPPED_CELLS",
|
|
152
|
+
bool,
|
|
153
|
+
"Checks for unmapped cells after synthesis and quits immediately if so.",
|
|
154
|
+
deprecated_names=["QUIT_ON_UNMAPPED_CELLS", "CHECK_UNMAPPED_CELLS"],
|
|
155
|
+
default=True,
|
|
156
|
+
)
|
|
157
|
+
config_vars = [error_on_var]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@Step.factory.register()
|
|
161
|
+
class YosysSynthChecks(MetricChecker):
|
|
162
|
+
id = "Checker.YosysSynthChecks"
|
|
163
|
+
name = "Yosys Synth Checks"
|
|
164
|
+
deferred = False
|
|
165
|
+
|
|
166
|
+
metric_name = "synthesis__check_error__count"
|
|
167
|
+
metric_description = "Yosys check errors"
|
|
168
|
+
error_on_var = Variable(
|
|
169
|
+
"ERROR_ON_SYNTH_CHECKS",
|
|
170
|
+
bool,
|
|
171
|
+
"Quits the flow immediately if one or more synthesis check errors are flagged. This checks for combinational loops and/or wires with no drivers.",
|
|
172
|
+
default=True,
|
|
173
|
+
deprecated_names=["QUIT_ON_SYNTH_CHECKS"],
|
|
174
|
+
)
|
|
175
|
+
config_vars = [error_on_var]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@Step.factory.register()
|
|
179
|
+
class TrDRC(MetricChecker):
|
|
180
|
+
id = "Checker.TrDRC"
|
|
181
|
+
name = "Routing DRC Checker"
|
|
182
|
+
long_name = "Routing Design Rule Checker"
|
|
183
|
+
|
|
184
|
+
metric_name = "route__drc_errors"
|
|
185
|
+
metric_description = "Routing DRC errors"
|
|
186
|
+
|
|
187
|
+
error_on_var = Variable(
|
|
188
|
+
"ERROR_ON_TR_DRC",
|
|
189
|
+
bool,
|
|
190
|
+
"Checks for DRC violations after routing and exits the flow if any was found.",
|
|
191
|
+
default=True,
|
|
192
|
+
deprecated_names=["QUIT_ON_TR_DRC"],
|
|
193
|
+
)
|
|
194
|
+
config_vars = [error_on_var]
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@Step.factory.register()
|
|
198
|
+
class MagicDRC(MetricChecker):
|
|
199
|
+
id = "Checker.MagicDRC"
|
|
200
|
+
name = "Magic DRC Checker"
|
|
201
|
+
long_name = "Magic Design Rule Checker"
|
|
202
|
+
|
|
203
|
+
metric_name = "magic__drc_error__count"
|
|
204
|
+
metric_description = "Magic DRC errors"
|
|
205
|
+
|
|
206
|
+
error_on_var = Variable(
|
|
207
|
+
"ERROR_ON_MAGIC_DRC",
|
|
208
|
+
bool,
|
|
209
|
+
"Checks for DRC violations after magic DRC is executed and exits the flow if any was found.",
|
|
210
|
+
default=True,
|
|
211
|
+
deprecated_names=["QUIT_ON_MAGIC_DRC"],
|
|
212
|
+
)
|
|
213
|
+
config_vars = [error_on_var]
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@Step.factory.register()
|
|
217
|
+
class IllegalOverlap(MetricChecker):
|
|
218
|
+
id = "Checker.IllegalOverlap"
|
|
219
|
+
name = "Illegal Overlap Checker"
|
|
220
|
+
long_name = "Spice Extraction-based Illegal Overlap Checker"
|
|
221
|
+
|
|
222
|
+
metric_name = "magic__illegal_overlap__count"
|
|
223
|
+
metric_description = "Magic Illegal Overlap errors"
|
|
224
|
+
|
|
225
|
+
error_on_var = Variable(
|
|
226
|
+
"ERROR_ON_ILLEGAL_OVERLAPS",
|
|
227
|
+
bool,
|
|
228
|
+
"Checks for illegal overlaps during Magic extraction. In some cases, these imply existing undetected shorts in the design. It raises an error at the end of the flow if so.",
|
|
229
|
+
default=True,
|
|
230
|
+
deprecated_names=["QUIT_ON_ILLEGAL_OVERLAPS"],
|
|
231
|
+
)
|
|
232
|
+
config_vars = [error_on_var]
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@Step.factory.register()
|
|
236
|
+
class DisconnectedPins(MetricChecker):
|
|
237
|
+
id = "Checker.DisconnectedPins"
|
|
238
|
+
name = "Disconnected Pins Checker"
|
|
239
|
+
deferred = False
|
|
240
|
+
|
|
241
|
+
metric_name = "design__critical_disconnected_pin__count"
|
|
242
|
+
metric_description = "critical disconnected pins"
|
|
243
|
+
|
|
244
|
+
error_on_var = Variable(
|
|
245
|
+
"ERROR_ON_DISCONNECTED_PINS",
|
|
246
|
+
bool,
|
|
247
|
+
"Checks for disconnected instance pins after detailed routing and quits immediately if so.",
|
|
248
|
+
default=True,
|
|
249
|
+
deprecated_names=["QUIT_ON_DISCONNECTED_PINS"],
|
|
250
|
+
)
|
|
251
|
+
config_vars = [error_on_var]
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
@Step.factory.register()
|
|
255
|
+
class WireLength(MetricChecker):
|
|
256
|
+
id = "Checker.WireLength"
|
|
257
|
+
name = "Wire Length Threshold Checker"
|
|
258
|
+
|
|
259
|
+
metric_name = "route__wirelength__max"
|
|
260
|
+
metric_description = "Threshold-surpassing long wires"
|
|
261
|
+
|
|
262
|
+
error_on_var = Variable(
|
|
263
|
+
"ERROR_ON_LONG_WIRE",
|
|
264
|
+
bool,
|
|
265
|
+
"Checks if any wire length exceeds the threshold set in the PDK. If so, an error is raised at the end of the flow.",
|
|
266
|
+
default=True,
|
|
267
|
+
deprecated_names=["QUIT_ON_LONG_WIRE"],
|
|
268
|
+
)
|
|
269
|
+
config_vars = [error_on_var]
|
|
270
|
+
|
|
271
|
+
def get_threshold(self) -> Optional[Decimal]:
|
|
272
|
+
threshold = self.config["WIRE_LENGTH_THRESHOLD"]
|
|
273
|
+
assert threshold is None or isinstance(threshold, Decimal)
|
|
274
|
+
return threshold
|
|
275
|
+
|
|
276
|
+
def get_threshold_description(self) -> Optional[str]:
|
|
277
|
+
return "the threshold specified in the configuration file."
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
@Step.factory.register()
|
|
281
|
+
class XOR(MetricChecker):
|
|
282
|
+
id = "Checker.XOR"
|
|
283
|
+
name = "XOR Difference Checker"
|
|
284
|
+
long_name = "Magic vs. KLayout XOR Difference Checker"
|
|
285
|
+
|
|
286
|
+
metric_name = "design__xor_difference__count"
|
|
287
|
+
metric_description = "XOR differences"
|
|
288
|
+
|
|
289
|
+
error_on_var = Variable(
|
|
290
|
+
"ERROR_ON_XOR_ERROR",
|
|
291
|
+
bool,
|
|
292
|
+
"Checks for geometric differences between the Magic and KLayout stream-outs. If any exist, raise an error at the end of the flow.",
|
|
293
|
+
default=True,
|
|
294
|
+
deprecated_names=["QUIT_ON_XOR_ERROR"],
|
|
295
|
+
)
|
|
296
|
+
config_vars = [error_on_var]
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@Step.factory.register()
|
|
300
|
+
class LVS(MetricChecker):
|
|
301
|
+
id = "Checker.LVS"
|
|
302
|
+
name = "LVS Error Checker"
|
|
303
|
+
long_name = "Layout vs. Schematic Error Checker"
|
|
304
|
+
|
|
305
|
+
metric_name = "design__lvs_error__count"
|
|
306
|
+
metric_description = "LVS errors"
|
|
307
|
+
|
|
308
|
+
error_on_var = Variable(
|
|
309
|
+
"ERROR_ON_LVS_ERROR",
|
|
310
|
+
bool,
|
|
311
|
+
"Checks for LVS errors after Netgen is executed. If any exist, it raises an error at the end of the flow.",
|
|
312
|
+
default=True,
|
|
313
|
+
deprecated_names=["QUIT_ON_LVS_ERROR"],
|
|
314
|
+
)
|
|
315
|
+
config_vars = [error_on_var]
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
@Step.factory.register()
|
|
319
|
+
class PowerGridViolations(MetricChecker):
|
|
320
|
+
id = "Checker.PowerGridViolations"
|
|
321
|
+
name = "Power Grid Violation Checker"
|
|
322
|
+
|
|
323
|
+
metric_name = "design__power_grid_violation__count"
|
|
324
|
+
metric_description = "power grid violations (as reported by OpenROAD PSM- you may ignore these if LVS passes)"
|
|
325
|
+
|
|
326
|
+
error_on_var = Variable(
|
|
327
|
+
"ERROR_ON_PDN_VIOLATIONS",
|
|
328
|
+
bool,
|
|
329
|
+
"Checks for unconnected nodes in the power grid. If any exists, an error is raised at the end of the flow.",
|
|
330
|
+
default=True,
|
|
331
|
+
deprecated_names=["QUIT_ON_PDN_VIOLATIONS", "FP_PDN_CHECK_NODES"],
|
|
332
|
+
)
|
|
333
|
+
config_vars = [error_on_var]
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
@Step.factory.register()
|
|
337
|
+
class LintErrors(MetricChecker):
|
|
338
|
+
id = "Checker.LintErrors"
|
|
339
|
+
name = "Lint Errors Checker"
|
|
340
|
+
long_name = "Lint Errors Checker"
|
|
341
|
+
deferred = False
|
|
342
|
+
|
|
343
|
+
metric_name = "design__lint_error__count"
|
|
344
|
+
metric_description = "Lint errors"
|
|
345
|
+
|
|
346
|
+
error_on_var = Variable(
|
|
347
|
+
"ERROR_ON_LINTER_ERRORS",
|
|
348
|
+
bool,
|
|
349
|
+
"Quit immediately on any linter errors.",
|
|
350
|
+
default=True,
|
|
351
|
+
deprecated_names=["QUIT_ON_VERILATOR_ERRORS", "QUIT_ON_LINTER_ERRORS"],
|
|
352
|
+
)
|
|
353
|
+
config_vars = [error_on_var]
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
@Step.factory.register()
|
|
357
|
+
class LintWarnings(MetricChecker):
|
|
358
|
+
id = "Checker.LintWarnings"
|
|
359
|
+
name = "Lint Warnings Checker"
|
|
360
|
+
long_name = "Lint Warnings Checker"
|
|
361
|
+
deferred = False
|
|
362
|
+
|
|
363
|
+
metric_name = "design__lint_warning__count"
|
|
364
|
+
metric_description = "Lint warnings"
|
|
365
|
+
|
|
366
|
+
error_on_var = Variable(
|
|
367
|
+
"ERROR_ON_LINTER_WARNINGS",
|
|
368
|
+
bool,
|
|
369
|
+
"Raise an error immediately on any linter warnings.",
|
|
370
|
+
default=False,
|
|
371
|
+
deprecated_names=["QUIT_ON_VERILATOR_WARNINGS", "QUIT_ON_LINTER_WARNINGS"],
|
|
372
|
+
)
|
|
373
|
+
config_vars = [error_on_var]
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
@Step.factory.register()
|
|
377
|
+
class LintTimingConstructs(MetricChecker):
|
|
378
|
+
id = "Checker.LintTimingConstructs"
|
|
379
|
+
name = "Lint Timing Error Checker"
|
|
380
|
+
long_name = "Lint Timing Errors Checker"
|
|
381
|
+
deferred = False
|
|
382
|
+
|
|
383
|
+
metric_name = "design__lint_timing_construct__count"
|
|
384
|
+
metric_description = "Lint Timing Errors"
|
|
385
|
+
|
|
386
|
+
error_on_var = Variable(
|
|
387
|
+
"ERROR_ON_LINTER_TIMING_CONSTRUCTS",
|
|
388
|
+
bool,
|
|
389
|
+
"Quit immediately on any discovered timing constructs during linting.",
|
|
390
|
+
default=True,
|
|
391
|
+
deprecated_names=["QUIT_ON_LINTER_TIMING_CONSTRUCTS"],
|
|
392
|
+
)
|
|
393
|
+
config_vars = [error_on_var]
|
|
394
|
+
|
|
395
|
+
def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
|
|
396
|
+
metric_value = state_in.metrics.get(self.metric_name)
|
|
397
|
+
|
|
398
|
+
if metric_value is not None:
|
|
399
|
+
if metric_value > 0:
|
|
400
|
+
error_msg = "Timing constructs found in the RTL. Please remove them or wrap them around an ifdef. It heavily unrecommended to rely on timing constructs for synthesis."
|
|
401
|
+
self.err(f"{error_msg}")
|
|
402
|
+
raise StepError(error_msg)
|
|
403
|
+
else:
|
|
404
|
+
info(f"Check for {self.metric_description} clear.")
|
|
405
|
+
else:
|
|
406
|
+
self.warn(
|
|
407
|
+
f"The {self.metric_description} metric was not found. Are you sure the relevant step was run?"
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
return {}, {}
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
@Step.factory.register()
|
|
414
|
+
class KLayoutDRC(MetricChecker):
|
|
415
|
+
id = "Checker.KLayoutDRC"
|
|
416
|
+
name = "KLayout DRC Checker"
|
|
417
|
+
long_name = "KLayout Design Rule Checker"
|
|
418
|
+
|
|
419
|
+
metric_name = "klayout__drc_error__count"
|
|
420
|
+
metric_description = "KLayout DRC errors"
|
|
421
|
+
|
|
422
|
+
error_on_var = Variable(
|
|
423
|
+
"ERROR_ON_KLAYOUT_DRC",
|
|
424
|
+
bool,
|
|
425
|
+
"Checks for DRC violations after KLayout DRC is executed and exits the flow if any was found.",
|
|
426
|
+
default=True,
|
|
427
|
+
deprecated_names=["QUIT_ON_KLAYOUT_DRC"],
|
|
428
|
+
)
|
|
429
|
+
config_vars = [error_on_var]
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
class TimingViolations(MetricChecker):
|
|
433
|
+
"""
|
|
434
|
+
Abstract class for timing violations.
|
|
435
|
+
|
|
436
|
+
This class creates `*_VIOLATION_CORNERS` variable for a subclass based on
|
|
437
|
+
with a name based on `violation_type`. The default value is `[""]` which
|
|
438
|
+
indicates matching no corners. This can be overriden by `corner_override`
|
|
439
|
+
|
|
440
|
+
:cvar violation_type:
|
|
441
|
+
Type of the timing violation. Used in log messages.
|
|
442
|
+
|
|
443
|
+
:cvar corner_override:
|
|
444
|
+
Overrides the subclass's `*_VIOLATION_CORNERS` variable.
|
|
445
|
+
|
|
446
|
+
:cvar match_none_wildcard:
|
|
447
|
+
Wildcard used to match no corners.
|
|
448
|
+
"""
|
|
449
|
+
|
|
450
|
+
name = "Timing Violations Checker"
|
|
451
|
+
long_name = "Timing Violations Checker"
|
|
452
|
+
|
|
453
|
+
violation_type: str = NotImplemented
|
|
454
|
+
match_none_wildcard = ""
|
|
455
|
+
corner_override: Optional[List[str]] = None
|
|
456
|
+
base_corner_var_name = "TIMING_VIOLATION_CORNERS"
|
|
457
|
+
|
|
458
|
+
def __init_subclass__(cls, **kwargs):
|
|
459
|
+
super().__init_subclass__(**kwargs)
|
|
460
|
+
|
|
461
|
+
cls.config_vars = cls.config_vars.copy()
|
|
462
|
+
cls.config_vars += [
|
|
463
|
+
Variable(
|
|
464
|
+
cls.base_corner_var_name,
|
|
465
|
+
List[str],
|
|
466
|
+
"A list of wildcards matching IPVT corners to use during checking for timing violations.",
|
|
467
|
+
pdk=True,
|
|
468
|
+
deprecated_names=["TIMING_VIOLATIONS_CORNERS"],
|
|
469
|
+
),
|
|
470
|
+
cls.get_corner_variable(),
|
|
471
|
+
]
|
|
472
|
+
|
|
473
|
+
@classmethod
|
|
474
|
+
def get_corner_variable(cls) -> Variable:
|
|
475
|
+
replace_by = cls.violation_type.upper().replace(" ", "_")
|
|
476
|
+
variable = Variable(
|
|
477
|
+
cls.base_corner_var_name.replace("TIMING", replace_by),
|
|
478
|
+
Optional[List[str]],
|
|
479
|
+
f"A list of wildcards matching IPVT corners to use during checking for {cls.violation_type} violations.",
|
|
480
|
+
)
|
|
481
|
+
if cls.corner_override:
|
|
482
|
+
variable.default = cls.corner_override
|
|
483
|
+
return variable
|
|
484
|
+
|
|
485
|
+
def get_corner_wildcards(self):
|
|
486
|
+
wildcards = self.config.get(self.get_corner_variable().name) or self.config.get(
|
|
487
|
+
self.base_corner_var_name
|
|
488
|
+
)
|
|
489
|
+
assert wildcards is not None
|
|
490
|
+
wildcards = [
|
|
491
|
+
wildcard
|
|
492
|
+
for wildcard in wildcards
|
|
493
|
+
if wildcard is not self.match_none_wildcard
|
|
494
|
+
]
|
|
495
|
+
return wildcards
|
|
496
|
+
|
|
497
|
+
def check_timing_violations(
|
|
498
|
+
self,
|
|
499
|
+
metric_basename: str,
|
|
500
|
+
state_in: State,
|
|
501
|
+
threshold: Optional[Decimal],
|
|
502
|
+
violation_type: str,
|
|
503
|
+
):
|
|
504
|
+
if not threshold:
|
|
505
|
+
threshold = Decimal(0)
|
|
506
|
+
|
|
507
|
+
metrics = {
|
|
508
|
+
key: value
|
|
509
|
+
for key, value in state_in.metrics.items()
|
|
510
|
+
if metric_basename in key
|
|
511
|
+
}
|
|
512
|
+
debug("Metrics ▶")
|
|
513
|
+
debug(metrics)
|
|
514
|
+
if not metrics:
|
|
515
|
+
self.warn(f"No metrics found for {metric_basename}.")
|
|
516
|
+
else:
|
|
517
|
+
metric_corners = set(
|
|
518
|
+
[parse_metric_modifiers(key)[1]["corner"] for key in metrics.keys()]
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
all_config_wildcards = set(self.get_corner_wildcards())
|
|
522
|
+
corner_filter = Filter(all_config_wildcards)
|
|
523
|
+
matched_config_wildcards: Set[str] = set()
|
|
524
|
+
for corner in metric_corners:
|
|
525
|
+
matched_config_wildcards.update(
|
|
526
|
+
corner_filter.get_matching_wildcards(corner)
|
|
527
|
+
)
|
|
528
|
+
unmatched_config_wildcards = all_config_wildcards - matched_config_wildcards
|
|
529
|
+
|
|
530
|
+
matched_corners = set(corner_filter.filter(metric_corners))
|
|
531
|
+
unmatched_corners = metric_corners - matched_corners
|
|
532
|
+
|
|
533
|
+
all_violating_corners = set(
|
|
534
|
+
[
|
|
535
|
+
corner
|
|
536
|
+
for corner in metric_corners
|
|
537
|
+
if metrics[f"{metric_basename}:{corner}"] > threshold
|
|
538
|
+
]
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
matched_violating_corners = all_violating_corners.intersection(
|
|
542
|
+
matched_corners
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
err_violating_corner = matched_violating_corners
|
|
546
|
+
warn_violating_corner = all_violating_corners - matched_violating_corners
|
|
547
|
+
|
|
548
|
+
debug("All corners ▶")
|
|
549
|
+
debug(metric_corners)
|
|
550
|
+
debug("Corners unmatched by config ▶")
|
|
551
|
+
debug(unmatched_corners)
|
|
552
|
+
debug("Violations at corners causing errors ▶")
|
|
553
|
+
debug(err_violating_corner)
|
|
554
|
+
debug("Violations at corners causing warnings ▶")
|
|
555
|
+
debug(warn_violating_corner)
|
|
556
|
+
|
|
557
|
+
err_msg = []
|
|
558
|
+
warn_msg = []
|
|
559
|
+
if len(unmatched_config_wildcards):
|
|
560
|
+
err_msg.append(
|
|
561
|
+
f"One or more wildcards specified in {self.get_corner_variable().name} did not match any corners:"
|
|
562
|
+
)
|
|
563
|
+
for wildcard in sorted(unmatched_config_wildcards):
|
|
564
|
+
err_msg.append(f"- {wildcard}")
|
|
565
|
+
|
|
566
|
+
if len(warn_violating_corner):
|
|
567
|
+
warn_msg.append(
|
|
568
|
+
f"{violation_type.title()} violations found in the following corners:"
|
|
569
|
+
)
|
|
570
|
+
for corner in sorted(warn_violating_corner):
|
|
571
|
+
warn_msg.append(f"* {corner}")
|
|
572
|
+
|
|
573
|
+
if err_violating_corner:
|
|
574
|
+
err_msg.append(
|
|
575
|
+
f"{violation_type.title()} violations found in the following corners:"
|
|
576
|
+
)
|
|
577
|
+
for corner in sorted(err_violating_corner):
|
|
578
|
+
err_msg.append(f"* {corner}")
|
|
579
|
+
|
|
580
|
+
if warn_msg:
|
|
581
|
+
self.warn("\n".join(warn_msg))
|
|
582
|
+
if not err_violating_corner:
|
|
583
|
+
verbose(f"No {violation_type} violations found")
|
|
584
|
+
if err_msg:
|
|
585
|
+
raise DeferredStepError("\n".join(err_msg))
|
|
586
|
+
|
|
587
|
+
def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
|
|
588
|
+
self.check_timing_violations(
|
|
589
|
+
f"{self.metric_name}__corner",
|
|
590
|
+
state_in,
|
|
591
|
+
self.get_threshold(),
|
|
592
|
+
self.violation_type,
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
return {}, {}
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
@Step.factory.register()
|
|
599
|
+
class SetupViolations(TimingViolations):
|
|
600
|
+
id = "Checker.SetupViolations"
|
|
601
|
+
name = "Setup Timing Violations Checker"
|
|
602
|
+
long_name = "Setup Timing Violations Checker"
|
|
603
|
+
violation_type = "setup"
|
|
604
|
+
|
|
605
|
+
metric_name = "timing__setup_vio__count"
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
@Step.factory.register()
|
|
609
|
+
class MaxCapViolations(TimingViolations):
|
|
610
|
+
id = "Checker.MaxCapViolations"
|
|
611
|
+
name = "Max Cap Violations Checker"
|
|
612
|
+
long_name = "Maximum Capacitance Violations Checker"
|
|
613
|
+
violation_type = "max cap"
|
|
614
|
+
|
|
615
|
+
metric_name = "design__max_cap_violation__count"
|
|
616
|
+
corner_override = [""]
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
@Step.factory.register()
|
|
620
|
+
class MaxSlewViolations(TimingViolations):
|
|
621
|
+
id = "Checker.MaxSlewViolations"
|
|
622
|
+
name = "Max Slew Violations Checker"
|
|
623
|
+
long_name = "Maximum Slew Violations Checker"
|
|
624
|
+
violation_type = "max slew"
|
|
625
|
+
|
|
626
|
+
metric_name = "design__max_slew_violation__count"
|
|
627
|
+
corner_override = [""]
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
@Step.factory.register()
|
|
631
|
+
class HoldViolations(TimingViolations):
|
|
632
|
+
id = "Checker.HoldViolations"
|
|
633
|
+
name = "Hold Timing Violations Checker"
|
|
634
|
+
long_name = "Hold Timing Violations Checker"
|
|
635
|
+
violation_type = "hold"
|
|
636
|
+
|
|
637
|
+
metric_name = "timing__hold_vio__count"
|