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,464 @@
|
|
|
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 os
|
|
16
|
+
import glob
|
|
17
|
+
import fnmatch
|
|
18
|
+
from enum import Enum
|
|
19
|
+
from decimal import Decimal
|
|
20
|
+
from types import SimpleNamespace
|
|
21
|
+
from typing import Any, Dict, List, Mapping, Sequence, Tuple, Union, Optional
|
|
22
|
+
|
|
23
|
+
from ..common import is_string
|
|
24
|
+
|
|
25
|
+
Keys = SimpleNamespace(
|
|
26
|
+
pdk_root="PDK_ROOT",
|
|
27
|
+
pdk="PDK",
|
|
28
|
+
pdkpath="PDKPATH",
|
|
29
|
+
scl="STD_CELL_LIBRARY",
|
|
30
|
+
design_dir="DESIGN_DIR",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
PROCESS_INFO_ALLOWLIST = [
|
|
34
|
+
Keys.pdk,
|
|
35
|
+
Keys.scl,
|
|
36
|
+
f"{Keys.scl}_OPT",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
Scalar = Union[str, int, Decimal, float, bool, None]
|
|
41
|
+
Valid = Union[Scalar, dict, list]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Expr(object):
|
|
45
|
+
class Token(object):
|
|
46
|
+
class Type(Enum):
|
|
47
|
+
VAR = 0
|
|
48
|
+
NUMBER = 1
|
|
49
|
+
OP = 2
|
|
50
|
+
LPAREN = 3
|
|
51
|
+
RPAREN = 4
|
|
52
|
+
|
|
53
|
+
def __init__(self, type: "Expr.Token.Type", value: str) -> None:
|
|
54
|
+
self.type: Expr.Token.Type = type
|
|
55
|
+
self.value: str = value
|
|
56
|
+
|
|
57
|
+
def __repr__(self):
|
|
58
|
+
return f"<Token:{self.type} '{self.value}'>"
|
|
59
|
+
|
|
60
|
+
def prec_assoc(self) -> Tuple[int, bool]:
|
|
61
|
+
"""
|
|
62
|
+
Returns (precedence, is_left_assoc)
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
if self.value in ["**"]:
|
|
66
|
+
return (20, False)
|
|
67
|
+
elif self.value in ["*", "/"]:
|
|
68
|
+
return (10, True)
|
|
69
|
+
elif self.value in ["+", "-"]:
|
|
70
|
+
return (0, True)
|
|
71
|
+
else:
|
|
72
|
+
raise TypeError(
|
|
73
|
+
f"pre-assoc not supported for non-token operators: '{self.value}'"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def tokenize(expr: str) -> List["Expr.Token"]:
|
|
78
|
+
rx_list = [
|
|
79
|
+
(re.compile(r"^\$([A-Za-z_][A-Za-z0-9_\.\[\]]*)"), Expr.Token.Type.VAR),
|
|
80
|
+
(re.compile(r"^(-?\d+\.?\d*)"), Expr.Token.Type.NUMBER),
|
|
81
|
+
(re.compile(r"^(\*\*)"), Expr.Token.Type.OP),
|
|
82
|
+
(re.compile(r"^(\+|\-|\*|\/)"), Expr.Token.Type.OP),
|
|
83
|
+
(re.compile(r"^(\()"), Expr.Token.Type.LPAREN),
|
|
84
|
+
(re.compile(r"^(\))"), Expr.Token.Type.RPAREN),
|
|
85
|
+
(re.compile(r"^\s+"), None),
|
|
86
|
+
]
|
|
87
|
+
tokens = []
|
|
88
|
+
str_so_far = expr
|
|
89
|
+
while not str_so_far.strip() == "":
|
|
90
|
+
found = False
|
|
91
|
+
|
|
92
|
+
for element in rx_list:
|
|
93
|
+
rx, type = element
|
|
94
|
+
m = rx.match(str_so_far)
|
|
95
|
+
if m is None:
|
|
96
|
+
continue
|
|
97
|
+
found = True
|
|
98
|
+
if type is not None:
|
|
99
|
+
tokens.append(Expr.Token(type, m[1]))
|
|
100
|
+
str_so_far = str_so_far[len(m[0]) :]
|
|
101
|
+
break
|
|
102
|
+
|
|
103
|
+
if not found:
|
|
104
|
+
raise SyntaxError(
|
|
105
|
+
f"Unexpected token at the start of the following string '{str_so_far}'."
|
|
106
|
+
)
|
|
107
|
+
return tokens
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def evaluate(expression: str, symbols: Mapping[str, Any]) -> Decimal:
|
|
111
|
+
tokens: List["Expr.Token"] = Expr.tokenize(expression)
|
|
112
|
+
ETT = Expr.Token.Type
|
|
113
|
+
|
|
114
|
+
# Infix to Postfix
|
|
115
|
+
postfix: List["Expr.Token"] = []
|
|
116
|
+
opstack: List["Expr.Token"] = []
|
|
117
|
+
for token in tokens:
|
|
118
|
+
if token.type == ETT.OP:
|
|
119
|
+
prec, assoc = token.prec_assoc()
|
|
120
|
+
|
|
121
|
+
top_prec = None
|
|
122
|
+
try:
|
|
123
|
+
top_prec, _ = opstack[-1].prec_assoc()
|
|
124
|
+
except TypeError:
|
|
125
|
+
pass
|
|
126
|
+
except IndexError:
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
while top_prec is not None and (
|
|
130
|
+
(assoc and prec <= top_prec) or (not assoc and prec < top_prec)
|
|
131
|
+
):
|
|
132
|
+
postfix.append(opstack.pop())
|
|
133
|
+
top_prec = None
|
|
134
|
+
try:
|
|
135
|
+
top_prec, _ = opstack[-1].prec_assoc()
|
|
136
|
+
except IndexError:
|
|
137
|
+
pass
|
|
138
|
+
opstack.append(token)
|
|
139
|
+
elif token.type == ETT.LPAREN:
|
|
140
|
+
opstack.append(token)
|
|
141
|
+
elif token.type == ETT.RPAREN:
|
|
142
|
+
top = opstack[-1]
|
|
143
|
+
while top.type != ETT.LPAREN:
|
|
144
|
+
postfix.append(top)
|
|
145
|
+
opstack.pop()
|
|
146
|
+
top = opstack[-1]
|
|
147
|
+
opstack.pop() # drop the LPAREN
|
|
148
|
+
else:
|
|
149
|
+
postfix.append(token)
|
|
150
|
+
|
|
151
|
+
while len(opstack):
|
|
152
|
+
postfix.append(opstack[-1])
|
|
153
|
+
opstack.pop()
|
|
154
|
+
|
|
155
|
+
# Evaluate
|
|
156
|
+
eval_stack = []
|
|
157
|
+
for token in postfix:
|
|
158
|
+
if token.type == ETT.NUMBER:
|
|
159
|
+
eval_stack.append(Decimal(token.value))
|
|
160
|
+
elif token.type == ETT.VAR:
|
|
161
|
+
try:
|
|
162
|
+
value = symbols[token.value]
|
|
163
|
+
if not (
|
|
164
|
+
isinstance(value, int)
|
|
165
|
+
or isinstance(value, float)
|
|
166
|
+
or isinstance(value, Decimal)
|
|
167
|
+
):
|
|
168
|
+
raise TypeError(
|
|
169
|
+
f"Referenced variable {token.value} is not of a valid numeric type: f{type(value)}"
|
|
170
|
+
)
|
|
171
|
+
eval_stack.append(Decimal(value))
|
|
172
|
+
except KeyError:
|
|
173
|
+
raise TypeError(
|
|
174
|
+
f"Configuration variable '{token.value}' not found."
|
|
175
|
+
)
|
|
176
|
+
elif token.type == ETT.OP:
|
|
177
|
+
try:
|
|
178
|
+
number1 = eval_stack[-2]
|
|
179
|
+
number2 = eval_stack[-1]
|
|
180
|
+
eval_stack.pop()
|
|
181
|
+
eval_stack.pop()
|
|
182
|
+
|
|
183
|
+
result = Decimal(0.0)
|
|
184
|
+
if token.value == "**":
|
|
185
|
+
result = number1**number2
|
|
186
|
+
elif token.value == "*":
|
|
187
|
+
result = number1 * number2
|
|
188
|
+
elif token.value == "/":
|
|
189
|
+
result = number1 / number2
|
|
190
|
+
elif token.value == "+":
|
|
191
|
+
result = number1 + number2
|
|
192
|
+
elif token.value == "-":
|
|
193
|
+
result = number1 + number2
|
|
194
|
+
|
|
195
|
+
eval_stack.append(result)
|
|
196
|
+
except IndexError:
|
|
197
|
+
raise SyntaxError(
|
|
198
|
+
f"not enough operands for operator '{token.value}'"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
if len(eval_stack) > 1:
|
|
202
|
+
raise ValueError("expression reduces to multiple values")
|
|
203
|
+
elif len(eval_stack) == 0:
|
|
204
|
+
raise ValueError("expression is empty")
|
|
205
|
+
|
|
206
|
+
return eval_stack[0]
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
ref_rx = re.compile(r"^\$([A-Za-z_][A-Za-z0-9_\.\[\]]*)")
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def process_string(
|
|
213
|
+
value: str,
|
|
214
|
+
symbols: Mapping[str, Any],
|
|
215
|
+
readable_paths: Optional[List[str]] = None,
|
|
216
|
+
) -> Valid:
|
|
217
|
+
global ref_rx
|
|
218
|
+
EXPR_PREFIX = "expr::"
|
|
219
|
+
REF_PREFIX = "ref::"
|
|
220
|
+
REFG_PREFIX = "refg::"
|
|
221
|
+
|
|
222
|
+
DIR_PREFIX = "dir::"
|
|
223
|
+
PDK_DIR_PREFIX = "pdk_dir::"
|
|
224
|
+
|
|
225
|
+
mutable: str = value
|
|
226
|
+
|
|
227
|
+
if value.startswith(DIR_PREFIX):
|
|
228
|
+
mutable = value.replace(DIR_PREFIX, f"refg::${Keys.design_dir}/")
|
|
229
|
+
elif value.startswith(PDK_DIR_PREFIX):
|
|
230
|
+
mutable = value.replace(PDK_DIR_PREFIX, f"refg::${Keys.pdkpath}/")
|
|
231
|
+
|
|
232
|
+
if mutable.startswith(EXPR_PREFIX):
|
|
233
|
+
try:
|
|
234
|
+
return Expr.evaluate(value[len(EXPR_PREFIX) :], symbols)
|
|
235
|
+
except SyntaxError as e:
|
|
236
|
+
raise SyntaxError(f"Invalid expression '{value}': {e}") from None
|
|
237
|
+
elif mutable.startswith(REF_PREFIX) or mutable.startswith(REFG_PREFIX):
|
|
238
|
+
reference = mutable[mutable.index("::") + 2 :]
|
|
239
|
+
match = ref_rx.match(reference)
|
|
240
|
+
if match is None:
|
|
241
|
+
raise SyntaxError(f"Invalid reference string '{reference}'") from None
|
|
242
|
+
|
|
243
|
+
reference_variable = match[1]
|
|
244
|
+
if reference_variable not in symbols:
|
|
245
|
+
raise KeyError(
|
|
246
|
+
f"Referenced variable '{reference_variable}' not found"
|
|
247
|
+
) from None
|
|
248
|
+
|
|
249
|
+
target = symbols[reference_variable]
|
|
250
|
+
if target is None:
|
|
251
|
+
return None
|
|
252
|
+
|
|
253
|
+
if not is_string(target):
|
|
254
|
+
if type(target) in [int, float, Decimal]:
|
|
255
|
+
raise TypeError(
|
|
256
|
+
f"Referenced variable {reference_variable} is a number and not a string: use expr::{match[0]} if you want to reference this number."
|
|
257
|
+
) from None
|
|
258
|
+
else:
|
|
259
|
+
raise TypeError(
|
|
260
|
+
f"Referenced variable {reference_variable} is not a valid string: {type(target)}."
|
|
261
|
+
) from None
|
|
262
|
+
|
|
263
|
+
target = str(target)
|
|
264
|
+
concatenated = reference.replace(match[0], target)
|
|
265
|
+
|
|
266
|
+
# Glob only if Refg
|
|
267
|
+
if not mutable.startswith(REFG_PREFIX):
|
|
268
|
+
return concatenated
|
|
269
|
+
|
|
270
|
+
## If we're refg, all returns beyond this point must be of type
|
|
271
|
+
## List[str]
|
|
272
|
+
|
|
273
|
+
# Glob only if readable_paths isn't null
|
|
274
|
+
if readable_paths is None:
|
|
275
|
+
return [target]
|
|
276
|
+
|
|
277
|
+
final_abspath = os.path.abspath(concatenated)
|
|
278
|
+
|
|
279
|
+
# Glob only if it doesn't already resolve to a valid file
|
|
280
|
+
if os.path.exists(final_abspath):
|
|
281
|
+
return [final_abspath]
|
|
282
|
+
|
|
283
|
+
in_exposed = [final_abspath.startswith(p) for p in readable_paths]
|
|
284
|
+
if True not in in_exposed:
|
|
285
|
+
raise PermissionError(
|
|
286
|
+
f"'{concatenated}' is not located any path readable to LibreLane"
|
|
287
|
+
)
|
|
288
|
+
files = sorted(glob.glob(final_abspath))
|
|
289
|
+
files_escaped = [file.replace("$", r"\$") for file in files]
|
|
290
|
+
files_escaped.sort()
|
|
291
|
+
|
|
292
|
+
if len(files_escaped) == 0:
|
|
293
|
+
files_escaped = [concatenated]
|
|
294
|
+
|
|
295
|
+
return files_escaped
|
|
296
|
+
else:
|
|
297
|
+
return mutable
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
PDK_PREFIX = "pdk::"
|
|
301
|
+
SCL_PREFIX = "scl::"
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def process_list_recursive(
|
|
305
|
+
input: Sequence[Any],
|
|
306
|
+
ref: List[Any],
|
|
307
|
+
symbols: Dict[str, Any],
|
|
308
|
+
readable_paths: Optional[List[str]],
|
|
309
|
+
*,
|
|
310
|
+
key_path: str = "",
|
|
311
|
+
):
|
|
312
|
+
for i, value in enumerate(input):
|
|
313
|
+
current_key_path = f"{key_path}[{i}]"
|
|
314
|
+
processed: Any = None
|
|
315
|
+
if isinstance(value, Mapping):
|
|
316
|
+
processed = {}
|
|
317
|
+
process_dict_recursive(
|
|
318
|
+
value,
|
|
319
|
+
processed,
|
|
320
|
+
symbols,
|
|
321
|
+
readable_paths,
|
|
322
|
+
key_path=current_key_path,
|
|
323
|
+
)
|
|
324
|
+
elif isinstance(value, Sequence) and not is_string(value):
|
|
325
|
+
processed = []
|
|
326
|
+
process_list_recursive(
|
|
327
|
+
value,
|
|
328
|
+
processed,
|
|
329
|
+
symbols,
|
|
330
|
+
readable_paths,
|
|
331
|
+
key_path=current_key_path,
|
|
332
|
+
)
|
|
333
|
+
elif is_string(value):
|
|
334
|
+
processed = process_string(value, symbols, readable_paths)
|
|
335
|
+
else:
|
|
336
|
+
processed = value
|
|
337
|
+
|
|
338
|
+
if processed is not None:
|
|
339
|
+
ref.append(processed)
|
|
340
|
+
symbols[current_key_path] = processed
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def process_dict_recursive(
|
|
344
|
+
input: Mapping[str, Any],
|
|
345
|
+
ref: Dict[str, Any],
|
|
346
|
+
symbols: Dict[str, Any],
|
|
347
|
+
readable_paths: Optional[List[str]],
|
|
348
|
+
*,
|
|
349
|
+
key_path: str = "",
|
|
350
|
+
):
|
|
351
|
+
for key, value in input.items():
|
|
352
|
+
current_key_path = key
|
|
353
|
+
if key_path != "":
|
|
354
|
+
current_key_path = f"{key_path}.{key}"
|
|
355
|
+
processed: Any = None
|
|
356
|
+
if isinstance(value, Mapping):
|
|
357
|
+
if key.startswith(PDK_PREFIX):
|
|
358
|
+
pdk_match = key[len(PDK_PREFIX) :]
|
|
359
|
+
if fnmatch.fnmatch(ref[Keys.pdk], pdk_match):
|
|
360
|
+
process_dict_recursive(
|
|
361
|
+
value,
|
|
362
|
+
ref,
|
|
363
|
+
symbols,
|
|
364
|
+
readable_paths,
|
|
365
|
+
key_path=key_path,
|
|
366
|
+
)
|
|
367
|
+
elif key.startswith(SCL_PREFIX):
|
|
368
|
+
scl_match = key[len(SCL_PREFIX) :]
|
|
369
|
+
if ref[Keys.scl] is not None and fnmatch.fnmatch(
|
|
370
|
+
ref[Keys.scl], scl_match
|
|
371
|
+
):
|
|
372
|
+
process_dict_recursive(
|
|
373
|
+
value,
|
|
374
|
+
ref,
|
|
375
|
+
symbols,
|
|
376
|
+
readable_paths,
|
|
377
|
+
key_path=key_path,
|
|
378
|
+
)
|
|
379
|
+
else:
|
|
380
|
+
processed = {}
|
|
381
|
+
process_dict_recursive(
|
|
382
|
+
value,
|
|
383
|
+
processed,
|
|
384
|
+
symbols,
|
|
385
|
+
readable_paths,
|
|
386
|
+
key_path=current_key_path,
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
elif isinstance(value, Sequence) and not is_string(value):
|
|
390
|
+
processed = []
|
|
391
|
+
process_list_recursive(
|
|
392
|
+
value,
|
|
393
|
+
processed,
|
|
394
|
+
symbols,
|
|
395
|
+
readable_paths,
|
|
396
|
+
key_path=current_key_path,
|
|
397
|
+
)
|
|
398
|
+
elif is_string(value):
|
|
399
|
+
processed = process_string(value, symbols, readable_paths)
|
|
400
|
+
else:
|
|
401
|
+
processed = value
|
|
402
|
+
|
|
403
|
+
if not key.startswith(PDK_PREFIX) and not key.startswith(SCL_PREFIX):
|
|
404
|
+
ref[key] = processed
|
|
405
|
+
symbols[current_key_path] = processed
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def process_config_dict(
|
|
409
|
+
config_in: Mapping[str, Any],
|
|
410
|
+
exposed_variables: Dict[str, Any],
|
|
411
|
+
readable_paths: Optional[List[str]],
|
|
412
|
+
) -> Dict[str, Any]:
|
|
413
|
+
state = dict(exposed_variables)
|
|
414
|
+
symbols = dict(exposed_variables)
|
|
415
|
+
process_dict_recursive(config_in, state, symbols, readable_paths)
|
|
416
|
+
return state
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def extract_process_vars(config_in: Dict[str, str]) -> Dict[str, str]:
|
|
420
|
+
return {
|
|
421
|
+
key: config_in[key]
|
|
422
|
+
for key in PROCESS_INFO_ALLOWLIST
|
|
423
|
+
if config_in.get(key) is not None and config_in.get(key) != ""
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def preprocess_dict(
|
|
428
|
+
config_dict: Mapping[str, Any],
|
|
429
|
+
design_dir: str,
|
|
430
|
+
only_extract_process_info: bool = False,
|
|
431
|
+
pdk: Optional[str] = None,
|
|
432
|
+
pdkpath: Optional[str] = None,
|
|
433
|
+
scl: Optional[str] = None,
|
|
434
|
+
readable_paths: Optional[List[str]] = None,
|
|
435
|
+
) -> Dict[str, Any]:
|
|
436
|
+
"""
|
|
437
|
+
If readable_paths are set to None, refg:: will not work
|
|
438
|
+
"""
|
|
439
|
+
if None in (pdk, pdkpath, scl):
|
|
440
|
+
if only_extract_process_info:
|
|
441
|
+
pdkpath = ""
|
|
442
|
+
scl = ""
|
|
443
|
+
pdk = ""
|
|
444
|
+
else:
|
|
445
|
+
raise TypeError(
|
|
446
|
+
"pdk, pdkpath and scl all need to be non-None unless only_extract_process_info is passed"
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
base_vars = {
|
|
450
|
+
Keys.pdk: pdk,
|
|
451
|
+
Keys.pdkpath: pdkpath,
|
|
452
|
+
Keys.scl: scl,
|
|
453
|
+
Keys.design_dir: design_dir,
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
preprocessed = process_config_dict(
|
|
457
|
+
config_dict,
|
|
458
|
+
base_vars,
|
|
459
|
+
readable_paths,
|
|
460
|
+
)
|
|
461
|
+
if only_extract_process_info:
|
|
462
|
+
preprocessed = extract_process_vars(preprocessed)
|
|
463
|
+
|
|
464
|
+
return preprocessed
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
from typing import Dict
|
|
15
|
+
|
|
16
|
+
removed_variables: Dict[str, str] = {
|
|
17
|
+
"PL_RANDOM_GLB_PLACEMENT": "The random global placer no longer yields a tangible benefit with newer versions of OpenROAD.",
|
|
18
|
+
"PL_RANDOM_INITIAL_PLACEMENT": "A random initial placer no longer yields a tangible benefit with newer versions of OpenROAD.",
|
|
19
|
+
"KLAYOUT_XOR_GDS": "The GDS output is of limited utility compared to the XML database.",
|
|
20
|
+
"KLAYOUT_XOR_XML": "The XML database is always generated.",
|
|
21
|
+
"MAGIC_GENERATE_GDS": "The GDS view is always generated when MAGIC_RUN_STREAMOUT is set.",
|
|
22
|
+
"CLOCK_BUFFER_FANOUT": "The simple CTS script that used this variable no longer exists.",
|
|
23
|
+
"FP_IO_HMETAL": "Replaced by FP_IO_HLAYER in the PDK configuration variables, which uses a more specific layer name.",
|
|
24
|
+
"FP_IO_VMETAL": "Replaced by FP_IO_VLAYER in the PDK configuration variables, which uses a more specific layer name.",
|
|
25
|
+
"GLB_OPTIMIZE_MIRRORING": "Shares DPL_OPTIMIZE_MIRRORING.",
|
|
26
|
+
"GRT_MAX_DIODE_INS_ITERS": "Relevant diode insertion strategies removed.",
|
|
27
|
+
"TAKE_LAYOUT_SCROT": "Buggy/dubious utility.",
|
|
28
|
+
"MAGIC_PAD": "Hacky/dubious utility.",
|
|
29
|
+
"GENERATE_FINAL_SUMMARY_REPORT": "To be specified via API/CLI- not much of a configuration variable.",
|
|
30
|
+
"USE_GPIO_PADS": "Add the pad's files to EXTRA_LEFS and EXTRA_VERILOG_MODELS as apprioriate.",
|
|
31
|
+
"PL_ESTIMATE_PARASITICS": "Parasitics are always estimated whenever possible.",
|
|
32
|
+
"GRT_ESTIMATE_PARASITICS": "Parasitics are always estimated whenever possible.",
|
|
33
|
+
"FP_PDN_AUTO_ADJUST": "Too situational. It's always best to be more explicit.",
|
|
34
|
+
"SYNTH_READ_BLACKBOX_LIB": "Changed to always be on.",
|
|
35
|
+
"CTS_TOLERANCE": "No longer supported by OpenROAD.",
|
|
36
|
+
"MAGIC_GDS_ALLOW_ABSTRACT": "Bad practice. If an abstract view is needed, a GDS file can be generated from the abstract LEF file.",
|
|
37
|
+
"PLACE_SITE_HEIGHT": "Now automatically extracted from PLACE_SITE.",
|
|
38
|
+
"PLACE_SITE_WIDTH": "Now automatically extracted from PLACE_SITE.",
|
|
39
|
+
"LEC_ENABLE": "Buggy/doesn't scale properly.",
|
|
40
|
+
"LVS_INSERT_POWER_PINS": "No longer necessary.",
|
|
41
|
+
"RUN_CVC": "Upstream no longer supports CVC for use within LibreLane.",
|
|
42
|
+
"FP_PADFRAME_CFG": "To be implemented.",
|
|
43
|
+
"FP_CONTEXT_DEF": "To be implemented.",
|
|
44
|
+
"FP_CONTEXT_LEF": "To be implemented.",
|
|
45
|
+
}
|