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,279 @@
|
|
|
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
|
+
import re
|
|
15
|
+
import textwrap
|
|
16
|
+
from enum import IntEnum
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from typing import (
|
|
19
|
+
List,
|
|
20
|
+
Mapping,
|
|
21
|
+
Tuple,
|
|
22
|
+
Dict,
|
|
23
|
+
Any,
|
|
24
|
+
Iterable,
|
|
25
|
+
Optional,
|
|
26
|
+
Union,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from .metric import Metric, MetricAggregator, MetricComparisonResult
|
|
30
|
+
from ..misc import Filter
|
|
31
|
+
|
|
32
|
+
modifier_rx = re.compile(r"([\w\-]+)\:([\w\-]+)")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TableVerbosity(IntEnum):
|
|
36
|
+
"""
|
|
37
|
+
The verbosity of the table: whether to include everything, just changes, only
|
|
38
|
+
bad changes or only critical change. Or just nothing.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
NONE = 0
|
|
42
|
+
CRITICAL = 1
|
|
43
|
+
WORSE = 2
|
|
44
|
+
CHANGED = 3
|
|
45
|
+
ALL = 4
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def parse_metric_modifiers(metric_name: str) -> Tuple[str, Mapping[str, str]]:
|
|
49
|
+
"""
|
|
50
|
+
Parses a metric name into a base and modifiers as specified in
|
|
51
|
+
the METRICS2.1 naming convention.
|
|
52
|
+
|
|
53
|
+
:param metric_name: The name of the metric as generated by a utility.
|
|
54
|
+
:returns: A tuple of the base part as a string, then the modifiers as
|
|
55
|
+
a key-value mapping.
|
|
56
|
+
"""
|
|
57
|
+
mn_mut = metric_name.split("__")
|
|
58
|
+
modifiers = {}
|
|
59
|
+
while ":" in mn_mut[-1]:
|
|
60
|
+
key, value = mn_mut.pop().split(":", maxsplit=1)
|
|
61
|
+
modifiers[key] = value
|
|
62
|
+
return "__".join(mn_mut), {k: modifiers[k] for k in reversed(modifiers)}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def aggregate_metrics(
|
|
66
|
+
input: Mapping[str, Any],
|
|
67
|
+
aggregator_by_metric: Optional[
|
|
68
|
+
Mapping[str, Union[MetricAggregator, Metric]]
|
|
69
|
+
] = None,
|
|
70
|
+
) -> Dict[str, Any]:
|
|
71
|
+
"""
|
|
72
|
+
Takes a set of metrics generated according to the METRICS2.1 naming
|
|
73
|
+
convention.
|
|
74
|
+
|
|
75
|
+
:param input: A mapping of strings to values of metrics.
|
|
76
|
+
:param aggregator_by_metric: A mapping of metric names to either:
|
|
77
|
+
- A tuple of the initial accumulator and reducer to aggregate the values from all modifier metrics
|
|
78
|
+
- A :class:`Metric` class
|
|
79
|
+
:returns: A tuple of the base part as a string, then the modifiers as
|
|
80
|
+
a key-value mapping.
|
|
81
|
+
"""
|
|
82
|
+
if aggregator_by_metric is None:
|
|
83
|
+
aggregator_by_metric = Metric.by_name
|
|
84
|
+
|
|
85
|
+
aggregated: Dict[str, Any] = {}
|
|
86
|
+
for name, value in input.items():
|
|
87
|
+
metric_name, modifiers = parse_metric_modifiers(name)
|
|
88
|
+
if len(modifiers) < 1:
|
|
89
|
+
# No modifiers = final aggregate, don't double-represent in sums
|
|
90
|
+
continue
|
|
91
|
+
|
|
92
|
+
modifier_names = list(modifiers.keys())
|
|
93
|
+
dont_aggregate: Iterable[str] = []
|
|
94
|
+
entry = aggregator_by_metric.get(metric_name)
|
|
95
|
+
if isinstance(entry, Metric):
|
|
96
|
+
dont_aggregate = entry.dont_aggregate or []
|
|
97
|
+
entry = entry.aggregator
|
|
98
|
+
|
|
99
|
+
if entry is None:
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
if len(set(modifier_names).intersection(set(dont_aggregate))):
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
metric_name_so_far = metric_name
|
|
106
|
+
for modifier in modifier_names:
|
|
107
|
+
start, aggregation_fn = entry
|
|
108
|
+
current = aggregated.get(metric_name_so_far) or start
|
|
109
|
+
aggregated[metric_name_so_far] = aggregation_fn([current, value])
|
|
110
|
+
metric_name_so_far += f"__{modifier}:{modifiers[modifier]}"
|
|
111
|
+
|
|
112
|
+
final_values = dict(input)
|
|
113
|
+
final_values.update(aggregated)
|
|
114
|
+
return final_values
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _key_from_metrics(fields: Iterable[str], metric: str) -> List[str]:
|
|
118
|
+
base, modifiers = parse_metric_modifiers(metric)
|
|
119
|
+
result = []
|
|
120
|
+
for field in fields:
|
|
121
|
+
if field == "":
|
|
122
|
+
result.append(base)
|
|
123
|
+
else:
|
|
124
|
+
result.append(modifiers.get(field, ""))
|
|
125
|
+
return result
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class MetricDiff(object):
|
|
129
|
+
"""
|
|
130
|
+
Aggregates a number of ``MetricComparisonResult`` and allows a number of
|
|
131
|
+
functions to be performed on them.
|
|
132
|
+
|
|
133
|
+
:param differences: The metric comparison results.
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
@dataclass
|
|
137
|
+
class MetricStatistics:
|
|
138
|
+
"""
|
|
139
|
+
A glorified namespace encapsulating a number of statistics of
|
|
140
|
+
:class:`MetricDiff`.
|
|
141
|
+
|
|
142
|
+
Should be generated using :meth:`MetricDiff.stats`.
|
|
143
|
+
|
|
144
|
+
:param better: The number of datapoints that represent a positive change.
|
|
145
|
+
:param worse: The number of datapoints that represent a negative change.
|
|
146
|
+
:param critical: The number of changes for critical metrics.
|
|
147
|
+
:param unchanged: Values that are unchanged.
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
better: int = 0
|
|
151
|
+
worse: int = 0
|
|
152
|
+
critical: int = 0
|
|
153
|
+
unchanged: int = 0
|
|
154
|
+
|
|
155
|
+
differences: List[MetricComparisonResult]
|
|
156
|
+
|
|
157
|
+
def __init__(self, differences: Iterable[MetricComparisonResult]) -> None:
|
|
158
|
+
self.differences = list(differences)
|
|
159
|
+
|
|
160
|
+
def render_md(
|
|
161
|
+
self,
|
|
162
|
+
sort_by: Optional[Iterable[str]] = None,
|
|
163
|
+
table_verbosity: TableVerbosity = TableVerbosity.ALL,
|
|
164
|
+
) -> str:
|
|
165
|
+
"""
|
|
166
|
+
:param sort_by: A list of tuples corresponding to modifiers to sort
|
|
167
|
+
metrics ascendingly by.
|
|
168
|
+
:param table_verbosity: The verbosity of the table: whether to include everything, just changes, only bad changes or only critical changes. Or just nothing.
|
|
169
|
+
:returns: A table of the differences in Markdown format.
|
|
170
|
+
"""
|
|
171
|
+
if table_verbosity == TableVerbosity.NONE:
|
|
172
|
+
return ""
|
|
173
|
+
|
|
174
|
+
differences = self.differences
|
|
175
|
+
if fields := sort_by:
|
|
176
|
+
differences = sorted(
|
|
177
|
+
differences,
|
|
178
|
+
key=lambda x: _key_from_metrics(fields, x.metric_name), # type: ignore # (mypy bug)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
table = ""
|
|
182
|
+
|
|
183
|
+
changed = []
|
|
184
|
+
worse = []
|
|
185
|
+
critical = []
|
|
186
|
+
remaining = []
|
|
187
|
+
|
|
188
|
+
for row in differences:
|
|
189
|
+
if row.critical is True:
|
|
190
|
+
critical.append(row)
|
|
191
|
+
elif row.better is False:
|
|
192
|
+
worse.append(row)
|
|
193
|
+
elif row.is_changed():
|
|
194
|
+
changed.append(row)
|
|
195
|
+
else:
|
|
196
|
+
remaining.append(row)
|
|
197
|
+
|
|
198
|
+
listed_differences: List[MetricComparisonResult] = []
|
|
199
|
+
if table_verbosity >= TableVerbosity.CRITICAL:
|
|
200
|
+
listed_differences += critical
|
|
201
|
+
if table_verbosity >= TableVerbosity.WORSE:
|
|
202
|
+
listed_differences += worse
|
|
203
|
+
if table_verbosity >= TableVerbosity.CHANGED:
|
|
204
|
+
listed_differences += changed
|
|
205
|
+
if table_verbosity >= TableVerbosity.ALL:
|
|
206
|
+
listed_differences += remaining
|
|
207
|
+
|
|
208
|
+
if len(listed_differences) > 0:
|
|
209
|
+
table = textwrap.dedent(
|
|
210
|
+
f"""
|
|
211
|
+
| {'Metric':<70} | {'Before':<10} | {'After':<10} | {'Delta':<20} |
|
|
212
|
+
| {'-':<70} | {'-':<10} | {'-':<10} | {'-':<20} |
|
|
213
|
+
"""
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
for row in listed_differences:
|
|
217
|
+
before, after, delta = row.format_values()
|
|
218
|
+
emoji = ""
|
|
219
|
+
if row.better is not None:
|
|
220
|
+
if row.better:
|
|
221
|
+
emoji = " ⭕"
|
|
222
|
+
else:
|
|
223
|
+
emoji = " ❗"
|
|
224
|
+
if row.critical and row.is_changed():
|
|
225
|
+
emoji = " ‼️"
|
|
226
|
+
table += f"| {row.metric_name:<70} | {before:<10} | {after:<10} | {f'{delta}{emoji}':<20} |\n"
|
|
227
|
+
|
|
228
|
+
return table
|
|
229
|
+
|
|
230
|
+
def stats(self) -> MetricStatistics:
|
|
231
|
+
"""
|
|
232
|
+
:returns: A :class:`MetricStatistics` object based on this aggregate.
|
|
233
|
+
"""
|
|
234
|
+
stats = MetricDiff.MetricStatistics()
|
|
235
|
+
for row in self.differences:
|
|
236
|
+
if not row.is_changed():
|
|
237
|
+
stats.unchanged += 1
|
|
238
|
+
elif row.better is not None:
|
|
239
|
+
if row.better:
|
|
240
|
+
stats.better += 1
|
|
241
|
+
else:
|
|
242
|
+
stats.worse += 1
|
|
243
|
+
if row.critical:
|
|
244
|
+
stats.critical += 1
|
|
245
|
+
return stats
|
|
246
|
+
|
|
247
|
+
@classmethod
|
|
248
|
+
def from_metrics(
|
|
249
|
+
Self,
|
|
250
|
+
gold: dict,
|
|
251
|
+
new: dict,
|
|
252
|
+
significant_figures: int,
|
|
253
|
+
filter: Filter = Filter(["*"]),
|
|
254
|
+
) -> "MetricDiff":
|
|
255
|
+
"""
|
|
256
|
+
Creates a :class:`MetricDiff` object from two sets of metrics.
|
|
257
|
+
|
|
258
|
+
:param gold: The "gold-standard" metrics to compare against
|
|
259
|
+
:param new: The metrics being evaluated
|
|
260
|
+
:param filter: A :class:`Filter` for the names of the metrics to include
|
|
261
|
+
or exclude certain metrics.
|
|
262
|
+
:returns: The aggregate of the differences between gold and good
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
def generator(g, n):
|
|
266
|
+
for metric in filter.filter(sorted(n.keys())):
|
|
267
|
+
if metric not in g:
|
|
268
|
+
continue
|
|
269
|
+
base_metric, modifiers = parse_metric_modifiers(metric)
|
|
270
|
+
lhs_value, rhs_value = g[metric], n[metric]
|
|
271
|
+
if type(lhs_value) != type(rhs_value):
|
|
272
|
+
lhs_value = type(rhs_value)(lhs_value)
|
|
273
|
+
|
|
274
|
+
if metric_object := Metric.by_name.get(base_metric):
|
|
275
|
+
yield metric_object.compare(
|
|
276
|
+
lhs_value, rhs_value, significant_figures, modifiers=modifiers
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
return MetricDiff(generator(gold, new))
|