librelane 3.0.0.dev24__py3-none-any.whl → 3.0.0.dev26__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/common/__init__.py +1 -0
- librelane/common/drc.py +1 -0
- librelane/common/misc.py +27 -5
- librelane/common/types.py +2 -3
- librelane/config/__main__.py +1 -1
- librelane/config/variable.py +16 -0
- librelane/examples/spm/config.yaml +8 -8
- librelane/examples/spm-user_project_wrapper/config.json +17 -5
- librelane/flows/flow.py +89 -21
- librelane/scripts/magic/def/mag_gds.tcl +1 -2
- librelane/scripts/magic/drc.tcl +0 -1
- librelane/scripts/magic/gds/extras_mag.tcl +0 -2
- librelane/scripts/magic/gds/mag_with_pointers.tcl +0 -1
- librelane/scripts/magic/lef/extras_maglef.tcl +0 -2
- librelane/scripts/magic/lef/maglef.tcl +0 -1
- librelane/scripts/magic/wrapper.tcl +2 -0
- librelane/scripts/odbpy/power_utils.py +8 -6
- librelane/scripts/odbpy/reader.py +2 -2
- librelane/scripts/openroad/common/io.tcl +23 -11
- librelane/scripts/openroad/common/pdn_cfg.tcl +36 -36
- librelane/scripts/openroad/ioplacer.tcl +22 -21
- librelane/scripts/openroad/pdn.tcl +1 -1
- librelane/state/state.py +11 -3
- librelane/steps/__main__.py +1 -2
- librelane/steps/common_variables.py +82 -33
- librelane/steps/magic.py +24 -14
- librelane/steps/odb.py +15 -39
- librelane/steps/openroad.py +22 -44
- librelane/steps/step.py +22 -7
- librelane/steps/tclstep.py +1 -1
- {librelane-3.0.0.dev24.dist-info → librelane-3.0.0.dev26.dist-info}/METADATA +1 -1
- {librelane-3.0.0.dev24.dist-info → librelane-3.0.0.dev26.dist-info}/RECORD +34 -34
- {librelane-3.0.0.dev24.dist-info → librelane-3.0.0.dev26.dist-info}/WHEEL +0 -0
- {librelane-3.0.0.dev24.dist-info → librelane-3.0.0.dev26.dist-info}/entry_points.txt +0 -0
|
@@ -15,50 +15,51 @@ source $::env(SCRIPTS_DIR)/openroad/common/io.tcl
|
|
|
15
15
|
read_current_odb
|
|
16
16
|
|
|
17
17
|
if { [info exists ::env(CONTEXTUAL_IO_FLAG)] } {
|
|
18
|
-
|
|
18
|
+
read_lef $::env(placement_tmpfiles)/top_level.lef
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
if { [info exists ::env(
|
|
22
|
-
|
|
21
|
+
if { [info exists ::env(IO_PIN_H_LENGTH)] } {
|
|
22
|
+
set_pin_length -hor_length $::env(IO_PIN_H_LENGTH)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
if { [info exists ::env(
|
|
26
|
-
|
|
25
|
+
if { [info exists ::env(IO_PIN_V_LENGTH)] } {
|
|
26
|
+
set_pin_length -ver_length $::env(IO_PIN_V_LENGTH)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
if { $::env(
|
|
30
|
-
|
|
29
|
+
if { $::env(IO_PIN_H_EXTENSION) != "0"} {
|
|
30
|
+
set_pin_length_extension -hor_extension $::env(IO_PIN_H_EXTENSION)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
if { $::env(
|
|
34
|
-
|
|
33
|
+
if { $::env(IO_PIN_V_EXTENSION) != "0"} {
|
|
34
|
+
set_pin_length_extension -ver_extension $::env(IO_PIN_V_EXTENSION)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
if {$::env(
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
if {$::env(IO_PIN_V_THICKNESS_MULT) != "" && $::env(IO_PIN_H_THICKNESS_MULT) != ""} {
|
|
38
|
+
set_pin_thick_multiplier\
|
|
39
|
+
-hor_multiplier $::env(IO_PIN_H_THICKNESS_MULT) \
|
|
40
|
+
-ver_multiplier $::env(IO_PIN_V_THICKNESS_MULT)
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
set arg_list [list]
|
|
43
|
-
if { $::env(
|
|
44
|
-
|
|
44
|
+
if { $::env(IO_PIN_PLACEMENT_MODE) == "random_equidistant" } {
|
|
45
|
+
lappend arg_list -random
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
if { [info exists ::env(
|
|
48
|
-
|
|
48
|
+
if { [info exists ::env(IO_PIN_MIN_DISTANCE)] } {
|
|
49
|
+
lappend arg_list -min_distance $::env(IO_PIN_MIN_DISTANCE)
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
if { $::env(
|
|
52
|
-
|
|
52
|
+
if { $::env(IO_PIN_PLACEMENT_MODE) == "annealing" } {
|
|
53
|
+
lappend arg_list -annealing
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
set HMETAL $::env(FP_IO_HLAYER)
|
|
56
57
|
set VMETAL $::env(FP_IO_VLAYER)
|
|
57
58
|
|
|
58
59
|
log_cmd place_pins {*}$arg_list \
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
-random_seed 42 \
|
|
61
|
+
-hor_layers $HMETAL \
|
|
62
|
+
-ver_layers $VMETAL
|
|
62
63
|
|
|
63
64
|
write_views
|
|
64
65
|
|
librelane/state/state.py
CHANGED
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
+
import io
|
|
16
17
|
import os
|
|
18
|
+
import csv
|
|
17
19
|
import sys
|
|
18
20
|
import json
|
|
19
21
|
import shutil
|
|
@@ -209,14 +211,20 @@ class State(GenericImmutableDict[str, StateElement]):
|
|
|
209
211
|
self._walk(self, path, visitor)
|
|
210
212
|
metrics_csv_path = os.path.join(path, "metrics.csv")
|
|
211
213
|
with open(metrics_csv_path, "w", encoding="utf8") as f:
|
|
212
|
-
|
|
213
|
-
for metric in self.metrics:
|
|
214
|
-
f.write(f"{metric},{self.metrics[metric]}\n")
|
|
214
|
+
self.metrics_to_csv(f)
|
|
215
215
|
|
|
216
216
|
metrics_json_path = os.path.join(path, "metrics.json")
|
|
217
217
|
with open(metrics_json_path, "w", encoding="utf8") as f:
|
|
218
218
|
f.write(self.metrics.dumps())
|
|
219
219
|
|
|
220
|
+
def metrics_to_csv(
|
|
221
|
+
self, fp: io.TextIOWrapper, metrics_object: Optional[Dict[str, Any]] = None
|
|
222
|
+
):
|
|
223
|
+
w = csv.writer(fp)
|
|
224
|
+
w.writerow(("Metric", "Value"))
|
|
225
|
+
for entry in (metrics_object or self.metrics).items():
|
|
226
|
+
w.writerow(entry)
|
|
227
|
+
|
|
220
228
|
def validate(self):
|
|
221
229
|
"""
|
|
222
230
|
Ensures that all paths exist in a State.
|
librelane/steps/__main__.py
CHANGED
|
@@ -15,7 +15,6 @@ import os
|
|
|
15
15
|
import shlex
|
|
16
16
|
import shutil
|
|
17
17
|
import datetime
|
|
18
|
-
import functools
|
|
19
18
|
import subprocess
|
|
20
19
|
from functools import partial
|
|
21
20
|
from typing import IO, Any, Dict, Optional, Sequence, Union
|
|
@@ -239,7 +238,7 @@ def eject(ctx, output, state_in, config, id):
|
|
|
239
238
|
found_stdin_data = found_stdin.read()
|
|
240
239
|
raise Stop()
|
|
241
240
|
|
|
242
|
-
step.run_subprocess =
|
|
241
|
+
step.run_subprocess = partial(
|
|
243
242
|
step.run_subprocess,
|
|
244
243
|
_popen_callable=popen_substitute,
|
|
245
244
|
)
|
|
@@ -18,204 +18,253 @@ from ..config import Variable
|
|
|
18
18
|
|
|
19
19
|
io_layer_variables = [
|
|
20
20
|
Variable(
|
|
21
|
-
"
|
|
21
|
+
"IO_PIN_V_EXTENSION",
|
|
22
22
|
Decimal,
|
|
23
23
|
"Extends the vertical io pins outside of the die by the specified units.",
|
|
24
24
|
default=0,
|
|
25
25
|
units="µm",
|
|
26
|
+
deprecated_names=["FP_IO_VEXTEND"],
|
|
26
27
|
),
|
|
27
28
|
Variable(
|
|
28
|
-
"
|
|
29
|
+
"IO_PIN_H_EXTENSION",
|
|
29
30
|
Decimal,
|
|
30
31
|
"Extends the horizontal io pins outside of the die by the specified units.",
|
|
31
32
|
default=0,
|
|
32
33
|
units="µm",
|
|
34
|
+
deprecated_names=["FP_IO_HEXTEND"],
|
|
33
35
|
),
|
|
34
36
|
Variable(
|
|
35
|
-
"
|
|
37
|
+
"IO_PIN_V_THICKNESS_MULT",
|
|
36
38
|
Decimal,
|
|
37
39
|
"A multiplier for vertical pin thickness. Base thickness is the pins layer min width.",
|
|
38
40
|
default=2,
|
|
41
|
+
deprecated_names=["FP_IO_VTHICKNESS_MULT"],
|
|
39
42
|
),
|
|
40
43
|
Variable(
|
|
41
|
-
"
|
|
44
|
+
"IO_PIN_H_THICKNESS_MULT",
|
|
42
45
|
Decimal,
|
|
43
46
|
"A multiplier for horizontal pin thickness. Base thickness is the pins layer min width.",
|
|
44
47
|
default=2,
|
|
48
|
+
deprecated_names=["FP_IO_HTHICKNESS_MULT"],
|
|
49
|
+
),
|
|
50
|
+
Variable(
|
|
51
|
+
"IO_PIN_V_LENGTH",
|
|
52
|
+
Optional[Decimal],
|
|
53
|
+
"""
|
|
54
|
+
The length of the pins with a north or south orientation. If unspecified by a PDK, OpenROAD will use whichever is higher of the following two values:
|
|
55
|
+
* The pin width
|
|
56
|
+
* The minimum value satisfying the minimum area constraint given the pin width
|
|
57
|
+
""",
|
|
58
|
+
units="µm",
|
|
59
|
+
pdk=True,
|
|
60
|
+
deprecated_names=["FP_IO_VLENGTH"],
|
|
61
|
+
),
|
|
62
|
+
Variable(
|
|
63
|
+
"IO_PIN_H_LENGTH",
|
|
64
|
+
Optional[Decimal],
|
|
65
|
+
"""
|
|
66
|
+
The length of the pins with an east or west orientation. If unspecified by a PDK, OpenROAD will use whichever is higher of the following two values:
|
|
67
|
+
* The pin width
|
|
68
|
+
* The minimum value satisfying the minimum area constraint given the pin width
|
|
69
|
+
""",
|
|
70
|
+
units="µm",
|
|
71
|
+
pdk=True,
|
|
72
|
+
deprecated_names=["FP_IO_HLENGTH"],
|
|
45
73
|
),
|
|
46
74
|
]
|
|
47
75
|
|
|
48
76
|
pdn_variables = [
|
|
49
77
|
Variable(
|
|
50
|
-
"
|
|
78
|
+
"PDN_SKIPTRIM",
|
|
51
79
|
bool,
|
|
52
80
|
"Enables `-skip_trim` option during pdngen which skips the metal trim step, which attempts to remove metal stubs.",
|
|
53
81
|
default=False,
|
|
82
|
+
deprecated_names=["FP_PDN_SKIPTRIM"],
|
|
54
83
|
),
|
|
55
84
|
Variable(
|
|
56
|
-
"
|
|
85
|
+
"PDN_CORE_RING",
|
|
57
86
|
bool,
|
|
58
87
|
"Enables adding a core ring around the design. More details on the control variables in the PDK config documentation.",
|
|
59
88
|
default=False,
|
|
89
|
+
deprecated_names=["FP_PDN_CORE_RING"],
|
|
60
90
|
),
|
|
61
91
|
Variable(
|
|
62
|
-
"
|
|
92
|
+
"PDN_ENABLE_RAILS",
|
|
63
93
|
bool,
|
|
64
94
|
"Enables the creation of rails in the power grid.",
|
|
65
95
|
default=True,
|
|
96
|
+
deprecated_names=["FP_PDN_ENABLE_RAILS"],
|
|
66
97
|
),
|
|
67
98
|
Variable(
|
|
68
|
-
"
|
|
99
|
+
"PDN_HORIZONTAL_HALO",
|
|
69
100
|
Decimal,
|
|
70
101
|
"Sets the horizontal halo around the macros during power grid insertion.",
|
|
71
102
|
default=10,
|
|
72
103
|
units="µm",
|
|
104
|
+
deprecated_names=["FP_PDN_HORIZONTAL_HALO"],
|
|
73
105
|
),
|
|
74
106
|
Variable(
|
|
75
|
-
"
|
|
107
|
+
"PDN_VERTICAL_HALO",
|
|
76
108
|
Decimal,
|
|
77
109
|
"Sets the vertical halo around the macros during power grid insertion.",
|
|
78
110
|
default=10,
|
|
79
111
|
units="µm",
|
|
112
|
+
deprecated_names=["FP_PDN_VERTICAL_HALO"],
|
|
80
113
|
),
|
|
81
114
|
Variable(
|
|
82
|
-
"
|
|
115
|
+
"PDN_MULTILAYER",
|
|
83
116
|
bool,
|
|
84
117
|
"Controls the layers used in the power grid. If set to false, only the lower layer will be used, which is useful when hardening a macro for integrating into a larger top-level design.",
|
|
85
118
|
default=True,
|
|
86
|
-
deprecated_names=["DESIGN_IS_CORE"],
|
|
119
|
+
deprecated_names=["FP_PDN_MULTILAYER", "DESIGN_IS_CORE"],
|
|
87
120
|
),
|
|
88
121
|
Variable(
|
|
89
|
-
"
|
|
122
|
+
"PDN_RAIL_OFFSET",
|
|
90
123
|
Decimal,
|
|
91
124
|
"The offset for the power distribution network rails for first metal layer.",
|
|
92
125
|
units="µm",
|
|
93
126
|
pdk=True,
|
|
127
|
+
deprecated_names=["FP_PDN_RAIL_OFFSET"],
|
|
94
128
|
),
|
|
95
129
|
Variable(
|
|
96
|
-
"
|
|
130
|
+
"PDN_VWIDTH",
|
|
97
131
|
Decimal,
|
|
98
132
|
"The strap width for the vertical layer in generated power distribution networks.",
|
|
99
133
|
units="µm",
|
|
100
134
|
pdk=True,
|
|
135
|
+
deprecated_names=["FP_PDN_VWIDTH"],
|
|
101
136
|
),
|
|
102
137
|
Variable(
|
|
103
|
-
"
|
|
138
|
+
"PDN_HWIDTH",
|
|
104
139
|
Decimal,
|
|
105
140
|
"The strap width for the horizontal layer in generated power distribution networks.",
|
|
106
141
|
units="µm",
|
|
107
142
|
pdk=True,
|
|
143
|
+
deprecated_names=["FP_PDN_HWIDTH"],
|
|
108
144
|
),
|
|
109
145
|
Variable(
|
|
110
|
-
"
|
|
146
|
+
"PDN_VSPACING",
|
|
111
147
|
Decimal,
|
|
112
148
|
"Intra-spacing (within a set) of vertical straps in generated power distribution networks.",
|
|
113
149
|
units="µm",
|
|
114
150
|
pdk=True,
|
|
151
|
+
deprecated_names=["FP_PDN_VSPACING"],
|
|
115
152
|
),
|
|
116
153
|
Variable(
|
|
117
|
-
"
|
|
154
|
+
"PDN_HSPACING",
|
|
118
155
|
Decimal,
|
|
119
156
|
"Intra-spacing (within a set) of horizontal straps in generated power distribution networks.",
|
|
120
157
|
units="µm",
|
|
121
158
|
pdk=True,
|
|
159
|
+
deprecated_names=["FP_PDN_HSPACING"],
|
|
122
160
|
),
|
|
123
161
|
Variable(
|
|
124
|
-
"
|
|
162
|
+
"PDN_VPITCH",
|
|
125
163
|
Decimal,
|
|
126
164
|
"Inter-distance (between sets) of vertical power straps in generated power distribution networks.",
|
|
127
165
|
units="µm",
|
|
128
166
|
pdk=True,
|
|
167
|
+
deprecated_names=["FP_PDN_VPITCH"],
|
|
129
168
|
),
|
|
130
169
|
Variable(
|
|
131
|
-
"
|
|
170
|
+
"PDN_HPITCH",
|
|
132
171
|
Decimal,
|
|
133
172
|
"Inter-distance (between sets) of horizontal power straps in generated power distribution networks.",
|
|
134
173
|
units="µm",
|
|
135
174
|
pdk=True,
|
|
175
|
+
deprecated_names=["FP_PDN_HPITCH"],
|
|
136
176
|
),
|
|
137
177
|
Variable(
|
|
138
|
-
"
|
|
178
|
+
"PDN_VOFFSET",
|
|
139
179
|
Decimal,
|
|
140
180
|
"Initial offset for sets of vertical power straps.",
|
|
141
181
|
units="µm",
|
|
142
182
|
pdk=True,
|
|
183
|
+
deprecated_names=["FP_PDN_VOFFSET"],
|
|
143
184
|
),
|
|
144
185
|
Variable(
|
|
145
|
-
"
|
|
186
|
+
"PDN_HOFFSET",
|
|
146
187
|
Decimal,
|
|
147
188
|
"Initial offset for sets of horizontal power straps.",
|
|
148
189
|
units="µm",
|
|
149
190
|
pdk=True,
|
|
191
|
+
deprecated_names=["FP_PDN_HOFFSET"],
|
|
150
192
|
),
|
|
151
193
|
Variable(
|
|
152
|
-
"
|
|
194
|
+
"PDN_CORE_RING_VWIDTH",
|
|
153
195
|
Decimal,
|
|
154
196
|
"The width for the vertical layer in the core ring of generated power distribution networks.",
|
|
155
197
|
units="µm",
|
|
156
198
|
pdk=True,
|
|
199
|
+
deprecated_names=["FP_PDN_CORE_RING_VWIDTH"],
|
|
157
200
|
),
|
|
158
201
|
Variable(
|
|
159
|
-
"
|
|
202
|
+
"PDN_CORE_RING_HWIDTH",
|
|
160
203
|
Decimal,
|
|
161
204
|
"The width for the horizontal layer in the core ring of generated power distribution networks.",
|
|
162
205
|
units="µm",
|
|
163
206
|
pdk=True,
|
|
207
|
+
deprecated_names=["FP_PDN_CORE_RING_HWIDTH"],
|
|
164
208
|
),
|
|
165
209
|
Variable(
|
|
166
|
-
"
|
|
210
|
+
"PDN_CORE_RING_VSPACING",
|
|
167
211
|
Decimal,
|
|
168
212
|
"The spacing for the vertical layer in the core ring of generated power distribution networks.",
|
|
169
213
|
units="µm",
|
|
170
214
|
pdk=True,
|
|
215
|
+
deprecated_names=["FP_PDN_CORE_RING_VSPACING"],
|
|
171
216
|
),
|
|
172
217
|
Variable(
|
|
173
|
-
"
|
|
218
|
+
"PDN_CORE_RING_HSPACING",
|
|
174
219
|
Decimal,
|
|
175
220
|
"The spacing for the horizontal layer in the core ring of generated power distribution networks.",
|
|
176
221
|
units="µm",
|
|
177
222
|
pdk=True,
|
|
223
|
+
deprecated_names=["FP_PDN_CORE_RING_HSPACING"],
|
|
178
224
|
),
|
|
179
225
|
Variable(
|
|
180
|
-
"
|
|
226
|
+
"PDN_CORE_RING_VOFFSET",
|
|
181
227
|
Decimal,
|
|
182
228
|
"The offset for the vertical layer in the core ring of generated power distribution networks.",
|
|
183
229
|
units="µm",
|
|
184
230
|
pdk=True,
|
|
231
|
+
deprecated_names=["FP_PDN_CORE_RING_VOFFSET"],
|
|
185
232
|
),
|
|
186
233
|
Variable(
|
|
187
|
-
"
|
|
234
|
+
"PDN_CORE_RING_HOFFSET",
|
|
188
235
|
Decimal,
|
|
189
236
|
"The offset for the horizontal layer in the core ring of generated power distribution networks.",
|
|
190
237
|
units="µm",
|
|
191
238
|
pdk=True,
|
|
239
|
+
deprecated_names=["FP_PDN_CORE_RING_HOFFSET"],
|
|
192
240
|
),
|
|
193
241
|
Variable(
|
|
194
|
-
"
|
|
242
|
+
"PDN_RAIL_LAYER",
|
|
195
243
|
str,
|
|
196
244
|
"Defines the metal layer used for PDN rails.",
|
|
197
|
-
deprecated_names=["FP_PDN_RAILS_LAYER"],
|
|
245
|
+
deprecated_names=["FP_PDN_RAIL_LAYER", "FP_PDN_RAILS_LAYER"],
|
|
198
246
|
pdk=True,
|
|
199
247
|
),
|
|
200
248
|
Variable(
|
|
201
|
-
"
|
|
249
|
+
"PDN_RAIL_WIDTH",
|
|
202
250
|
Decimal,
|
|
203
251
|
"Defines the width of PDN rails on the `FP_PDN_RAILS_LAYER` layer.",
|
|
204
252
|
units="µm",
|
|
205
253
|
pdk=True,
|
|
254
|
+
deprecated_names=["FP_PDN_RAIL_WIDTH"],
|
|
206
255
|
),
|
|
207
256
|
Variable(
|
|
208
|
-
"
|
|
257
|
+
"PDN_HORIZONTAL_LAYER",
|
|
209
258
|
str,
|
|
210
259
|
"Defines the horizontal PDN layer.",
|
|
211
|
-
deprecated_names=["FP_PDN_UPPER_LAYER"],
|
|
260
|
+
deprecated_names=["FP_PDN_HORIZONTAL_LAYER", "FP_PDN_UPPER_LAYER"],
|
|
212
261
|
pdk=True,
|
|
213
262
|
),
|
|
214
263
|
Variable(
|
|
215
|
-
"
|
|
264
|
+
"PDN_VERTICAL_LAYER",
|
|
216
265
|
str,
|
|
217
266
|
"Defines the vertical PDN layer.",
|
|
218
|
-
deprecated_names=["FP_PDN_LOWER_LAYER"],
|
|
267
|
+
deprecated_names=["FP_PDN_VERTICAL_LAYER", "FP_PDN_LOWER_LAYER"],
|
|
219
268
|
pdk=True,
|
|
220
269
|
),
|
|
221
270
|
]
|
librelane/steps/magic.py
CHANGED
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
import os
|
|
15
15
|
import re
|
|
16
16
|
import shutil
|
|
17
|
-
import functools
|
|
18
17
|
import subprocess
|
|
19
18
|
from signal import SIGKILL
|
|
20
19
|
from decimal import Decimal
|
|
@@ -34,7 +33,8 @@ from .tclstep import TclStep
|
|
|
34
33
|
from ..state import DesignFormat, State
|
|
35
34
|
|
|
36
35
|
from ..config import Variable
|
|
37
|
-
from ..common import get_script_dir, DRC as DRCObject, Path, mkdirp
|
|
36
|
+
from ..common import get_script_dir, DRC as DRCObject, Path, mkdirp, count_occurences
|
|
37
|
+
from ..logging import warn
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
DesignFormat(
|
|
@@ -480,6 +480,12 @@ class SpiceExtraction(MagicStep):
|
|
|
480
480
|
"Extracts a SPICE netlist based on black-boxed standard cells and macros (basically, anything with a LEF) rather than transistors. An error will be thrown if both this and `MAGIC_EXT_USE_GDS` is set to ``True``.",
|
|
481
481
|
default=False,
|
|
482
482
|
),
|
|
483
|
+
Variable(
|
|
484
|
+
"MAGIC_FEEDBACK_CONVERSION_THRESHOLD",
|
|
485
|
+
int,
|
|
486
|
+
"If Magic provides more feedback items than this threshold, conversion to KLayout databases is skipped (as something has gone horribly wrong.)",
|
|
487
|
+
default=10000,
|
|
488
|
+
),
|
|
483
489
|
]
|
|
484
490
|
|
|
485
491
|
def get_script_path(self):
|
|
@@ -497,22 +503,29 @@ class SpiceExtraction(MagicStep):
|
|
|
497
503
|
|
|
498
504
|
views_updates, metrics_updates = super().run(state_in, env=env, **kwargs)
|
|
499
505
|
|
|
500
|
-
cif_scale = Decimal(open(os.path.join(self.step_dir, "cif_scale.txt")).read())
|
|
501
506
|
feedback_path = os.path.join(self.step_dir, "feedback.txt")
|
|
507
|
+
with open(feedback_path, encoding="utf8") as f:
|
|
508
|
+
illegal_overlap_count = count_occurences(f, "Illegal overlap")
|
|
509
|
+
|
|
510
|
+
metrics_updates["magic__illegal_overlap__count"] = illegal_overlap_count
|
|
511
|
+
threshold = self.config["MAGIC_FEEDBACK_CONVERSION_THRESHOLD"]
|
|
512
|
+
if illegal_overlap_count > threshold:
|
|
513
|
+
warn(
|
|
514
|
+
f"Not converting the feedback to the KLayout database format: {illegal_overlap_count} > MAGIC_FEEDBACK_CONVERSION_THRESHOLD ({threshold}). You may manually increase the threshold, but it might take forever."
|
|
515
|
+
)
|
|
516
|
+
return views_updates, metrics_updates
|
|
517
|
+
|
|
518
|
+
cif_scale = Decimal(open(os.path.join(self.step_dir, "cif_scale.txt")).read())
|
|
502
519
|
try:
|
|
503
520
|
se_feedback, _ = DRCObject.from_magic_feedback(
|
|
504
521
|
open(feedback_path, encoding="utf8"),
|
|
505
522
|
cif_scale,
|
|
506
523
|
self.config["DESIGN_NAME"],
|
|
507
524
|
)
|
|
508
|
-
illegal_overlap_count =
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
for v in se_feedback.violations.values()
|
|
513
|
-
if "Illegal overlap" in v.description
|
|
514
|
-
],
|
|
515
|
-
0,
|
|
525
|
+
illegal_overlap_count = sum(
|
|
526
|
+
len(v.bounding_boxes)
|
|
527
|
+
for v in se_feedback.violations.values()
|
|
528
|
+
if "Illegal overlap" in v.description
|
|
516
529
|
)
|
|
517
530
|
with open(os.path.join(self.step_dir, "feedback.xml"), "wb") as f:
|
|
518
531
|
se_feedback.to_klayout_xml(f)
|
|
@@ -521,9 +534,6 @@ class SpiceExtraction(MagicStep):
|
|
|
521
534
|
self.warn(
|
|
522
535
|
f"Failed to convert SPICE extraction feedback to KLayout database format: {e}"
|
|
523
536
|
)
|
|
524
|
-
metrics_updates["magic__illegal_overlap__count"] = (
|
|
525
|
-
open(feedback_path, encoding="utf8").read().count("Illegal overlap")
|
|
526
|
-
)
|
|
527
537
|
return views_updates, metrics_updates
|
|
528
538
|
|
|
529
539
|
|
librelane/steps/odb.py
CHANGED
|
@@ -17,7 +17,6 @@ import json
|
|
|
17
17
|
import shutil
|
|
18
18
|
from math import inf
|
|
19
19
|
from decimal import Decimal
|
|
20
|
-
from functools import reduce
|
|
21
20
|
from abc import abstractmethod
|
|
22
21
|
from dataclasses import dataclass
|
|
23
22
|
from typing import Dict, List, Literal, Optional, Tuple
|
|
@@ -418,9 +417,7 @@ class ManualMacroPlacement(OdbpyStep):
|
|
|
418
417
|
)
|
|
419
418
|
shutil.copyfile(cfg_ref, cfg_file)
|
|
420
419
|
elif macros := self.config.get("MACROS"):
|
|
421
|
-
instance_count =
|
|
422
|
-
lambda x, y: x + len(y.instances), macros.values(), 0
|
|
423
|
-
)
|
|
420
|
+
instance_count = sum(len(m.instances) for m in macros.values())
|
|
424
421
|
if instance_count >= 1:
|
|
425
422
|
with open(cfg_file, "w") as f:
|
|
426
423
|
for module, macro in macros.items():
|
|
@@ -618,31 +615,10 @@ class CustomIOPlacement(OdbpyStep):
|
|
|
618
615
|
|
|
619
616
|
config_vars = io_layer_variables + [
|
|
620
617
|
Variable(
|
|
621
|
-
"
|
|
622
|
-
Optional[Decimal],
|
|
623
|
-
"""
|
|
624
|
-
The length of the pins with a north or south orientation. If unspecified by a PDK, the script will use whichever is higher of the following two values:
|
|
625
|
-
* The pin width
|
|
626
|
-
* The minimum value satisfying the minimum area constraint given the pin width
|
|
627
|
-
""",
|
|
628
|
-
units="µm",
|
|
629
|
-
pdk=True,
|
|
630
|
-
),
|
|
631
|
-
Variable(
|
|
632
|
-
"FP_IO_HLENGTH",
|
|
633
|
-
Optional[Decimal],
|
|
634
|
-
"""
|
|
635
|
-
The length of the pins with an east or west orientation. If unspecified by a PDK, the script will use whichever is higher of the following two values:
|
|
636
|
-
* The pin width
|
|
637
|
-
* The minimum value satisfying the minimum area constraint given the pin width
|
|
638
|
-
""",
|
|
639
|
-
units="µm",
|
|
640
|
-
pdk=True,
|
|
641
|
-
),
|
|
642
|
-
Variable(
|
|
643
|
-
"FP_PIN_ORDER_CFG",
|
|
618
|
+
"IO_PIN_ORDER_CFG",
|
|
644
619
|
Optional[Path],
|
|
645
|
-
"Path to
|
|
620
|
+
"Path to a custom pin configuration file.",
|
|
621
|
+
deprecated_names=["FP_PIN_ORDER_CFG"],
|
|
646
622
|
),
|
|
647
623
|
Variable(
|
|
648
624
|
"ERRORS_ON_UNMATCHED_IO",
|
|
@@ -660,28 +636,28 @@ class CustomIOPlacement(OdbpyStep):
|
|
|
660
636
|
|
|
661
637
|
def get_command(self) -> List[str]:
|
|
662
638
|
length_args = []
|
|
663
|
-
if self.config["
|
|
664
|
-
length_args += ["--ver-length", self.config["
|
|
665
|
-
if self.config["
|
|
666
|
-
length_args += ["--hor-length", self.config["
|
|
639
|
+
if self.config["IO_PIN_V_LENGTH"] is not None:
|
|
640
|
+
length_args += ["--ver-length", self.config["IO_PIN_V_LENGTH"]]
|
|
641
|
+
if self.config["IO_PIN_H_LENGTH"] is not None:
|
|
642
|
+
length_args += ["--hor-length", self.config["IO_PIN_H_LENGTH"]]
|
|
667
643
|
|
|
668
644
|
return (
|
|
669
645
|
super().get_command()
|
|
670
646
|
+ [
|
|
671
647
|
"--config",
|
|
672
|
-
self.config["
|
|
648
|
+
self.config["IO_PIN_ORDER_CFG"],
|
|
673
649
|
"--hor-layer",
|
|
674
650
|
self.config["FP_IO_HLAYER"],
|
|
675
651
|
"--ver-layer",
|
|
676
652
|
self.config["FP_IO_VLAYER"],
|
|
677
653
|
"--hor-width-mult",
|
|
678
|
-
str(self.config["
|
|
654
|
+
str(self.config["IO_PIN_V_THICKNESS_MULT"]),
|
|
679
655
|
"--ver-width-mult",
|
|
680
|
-
str(self.config["
|
|
656
|
+
str(self.config["IO_PIN_H_THICKNESS_MULT"]),
|
|
681
657
|
"--hor-extension",
|
|
682
|
-
str(self.config["
|
|
658
|
+
str(self.config["IO_PIN_H_EXTENSION"]),
|
|
683
659
|
"--ver-extension",
|
|
684
|
-
str(self.config["
|
|
660
|
+
str(self.config["IO_PIN_V_EXTENSION"]),
|
|
685
661
|
"--unmatched-error",
|
|
686
662
|
self.config["ERRORS_ON_UNMATCHED_IO"],
|
|
687
663
|
]
|
|
@@ -689,8 +665,8 @@ class CustomIOPlacement(OdbpyStep):
|
|
|
689
665
|
)
|
|
690
666
|
|
|
691
667
|
def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
|
|
692
|
-
if self.config["
|
|
693
|
-
info(f"No custom
|
|
668
|
+
if self.config["IO_PIN_ORDER_CFG"] is None:
|
|
669
|
+
info(f"No custom I/O placement file configured, skipping '{self.id}'…")
|
|
694
670
|
return {}, {}
|
|
695
671
|
return super().run(state_in, **kwargs)
|
|
696
672
|
|