librelane 2.4.0.dev0__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 +470 -0
- librelane/__version__.py +43 -0
- librelane/common/__init__.py +61 -0
- librelane/common/cli.py +75 -0
- librelane/common/drc.py +245 -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 +402 -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 +117 -0
- librelane/config/__init__.py +32 -0
- librelane/config/__main__.py +158 -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 +722 -0
- librelane/container.py +264 -0
- librelane/env_info.py +306 -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 +330 -0
- librelane/flows/cli.py +463 -0
- librelane/flows/flow.py +985 -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/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 +81 -0
- librelane/scripts/magic/drc.tcl +79 -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 +47 -0
- librelane/scripts/magic/gds/mag_with_pointers.tcl +32 -0
- librelane/scripts/magic/get_bbox.tcl +11 -0
- librelane/scripts/magic/lef/extras_maglef.tcl +63 -0
- librelane/scripts/magic/lef/maglef.tcl +27 -0
- librelane/scripts/magic/lef.tcl +57 -0
- librelane/scripts/magic/open.tcl +28 -0
- librelane/scripts/magic/wrapper.tcl +19 -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 +574 -0
- librelane/scripts/odbpy/diodes.py +373 -0
- librelane/scripts/odbpy/disconnected_pins.py +305 -0
- librelane/scripts/odbpy/exception_codes.py +17 -0
- librelane/scripts/odbpy/filter_unannotated.py +100 -0
- librelane/scripts/odbpy/io_place.py +482 -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 +395 -0
- librelane/scripts/odbpy/random_place.py +57 -0
- librelane/scripts/odbpy/reader.py +246 -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 +476 -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 +15 -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 +180 -0
- librelane/state/state.py +351 -0
- librelane/steps/__init__.py +61 -0
- librelane/steps/__main__.py +511 -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 +566 -0
- librelane/steps/misc.py +160 -0
- librelane/steps/netgen.py +253 -0
- librelane/steps/odb.py +955 -0
- librelane/steps/openroad.py +2433 -0
- librelane/steps/openroad_alerts.py +102 -0
- librelane/steps/pyosys.py +629 -0
- librelane/steps/step.py +1547 -0
- librelane/steps/tclstep.py +288 -0
- librelane/steps/verilator.py +222 -0
- librelane/steps/yosys.py +371 -0
- librelane-2.4.0.dev0.dist-info/METADATA +151 -0
- librelane-2.4.0.dev0.dist-info/RECORD +166 -0
- librelane-2.4.0.dev0.dist-info/WHEEL +4 -0
- librelane-2.4.0.dev0.dist-info/entry_points.txt +8 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright 2020 Efabless Corporation
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
import odb
|
|
16
|
+
|
|
17
|
+
from reader import OdbReader, click_odb, click
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command()
|
|
21
|
+
@click.option(
|
|
22
|
+
"-n",
|
|
23
|
+
"--netlist-def",
|
|
24
|
+
required=True,
|
|
25
|
+
help="DEF view of the design that has the connectivity information",
|
|
26
|
+
)
|
|
27
|
+
@click.option("-v", "--verbose", default=False, is_flag=True)
|
|
28
|
+
@click.option("-a", "--all-shapes", default=False, is_flag=True)
|
|
29
|
+
@click.option(
|
|
30
|
+
"-N",
|
|
31
|
+
"--pad-pin-name",
|
|
32
|
+
default="PAD",
|
|
33
|
+
help="Name of the pin of the pad as it appears in the netlist def.",
|
|
34
|
+
)
|
|
35
|
+
@click.option(
|
|
36
|
+
"-m",
|
|
37
|
+
"--map",
|
|
38
|
+
default="",
|
|
39
|
+
help="Semicolon;delimited extra mappings that are hard to infer from the netlist def. Format: -extra pad_instance_name pad_pin block_pin (INPUT|OUTPUT|INOUT)",
|
|
40
|
+
)
|
|
41
|
+
@click_odb
|
|
42
|
+
def label_macro_pins(
|
|
43
|
+
netlist_def,
|
|
44
|
+
verbose,
|
|
45
|
+
all_shapes,
|
|
46
|
+
pad_pin_name,
|
|
47
|
+
map,
|
|
48
|
+
input_lefs,
|
|
49
|
+
reader,
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
Takes a DEF file with no PINS section, a LEF file that has the shapes of all
|
|
53
|
+
i/o ports that need to labeled, and a netlist where i/o ports are connected
|
|
54
|
+
to single macro pin given also as an input -> writes a PINS section with shapes
|
|
55
|
+
generated over those macro pins, "labels".
|
|
56
|
+
"""
|
|
57
|
+
top = reader
|
|
58
|
+
|
|
59
|
+
extra_mappings = []
|
|
60
|
+
extra_mappings_pin_names = []
|
|
61
|
+
if map != "":
|
|
62
|
+
extra_mappings = [tuple(m.split()) for m in map.split(";")]
|
|
63
|
+
extra_mappings_pin_names = [tup[2] for tup in extra_mappings]
|
|
64
|
+
|
|
65
|
+
def getBiggestBox(iterm):
|
|
66
|
+
inst = iterm.getInst()
|
|
67
|
+
px, py = inst.getOrigin()
|
|
68
|
+
orient = inst.getOrient()
|
|
69
|
+
transform = odb.dbTransform(orient, odb.Point(px, py))
|
|
70
|
+
|
|
71
|
+
mterm = iterm.getMTerm()
|
|
72
|
+
mpins = mterm.getMPins()
|
|
73
|
+
|
|
74
|
+
# label biggest mpin
|
|
75
|
+
biggest_mpin = None
|
|
76
|
+
biggest_size = -1
|
|
77
|
+
for i in range(len(mpins)):
|
|
78
|
+
mpin = mpins[i]
|
|
79
|
+
box = mpin.getGeometry()[
|
|
80
|
+
0
|
|
81
|
+
] # assumes there's only one; to extend and get biggest
|
|
82
|
+
|
|
83
|
+
llx, lly = box.xMin(), box.yMin()
|
|
84
|
+
urx, ury = box.xMax(), box.yMax()
|
|
85
|
+
area = (urx - llx) * (ury - lly)
|
|
86
|
+
if area > biggest_size:
|
|
87
|
+
biggest_size = area
|
|
88
|
+
biggest_mpin = mpin
|
|
89
|
+
|
|
90
|
+
main_mpin = biggest_mpin
|
|
91
|
+
box = main_mpin.getGeometry()[0]
|
|
92
|
+
ll = odb.Point(box.xMin(), box.yMin())
|
|
93
|
+
ur = odb.Point(box.xMax(), box.yMax())
|
|
94
|
+
# x = (ll.getX() + ur.getX())//2
|
|
95
|
+
# y = (ll.getY() + ur.getY())//2
|
|
96
|
+
# if VERBOSE: print(x, y)
|
|
97
|
+
transform.apply(ll)
|
|
98
|
+
transform.apply(ur)
|
|
99
|
+
|
|
100
|
+
layer = box.getTechLayer()
|
|
101
|
+
|
|
102
|
+
return [(layer, ll, ur)]
|
|
103
|
+
|
|
104
|
+
def getAllBoxes(iterm):
|
|
105
|
+
inst = iterm.getInst()
|
|
106
|
+
px, py = inst.getOrigin()
|
|
107
|
+
orient = inst.getOrient()
|
|
108
|
+
transform = odb.dbTransform(orient, odb.Point(px, py))
|
|
109
|
+
|
|
110
|
+
mterm = iterm.getMTerm()
|
|
111
|
+
mpins = mterm.getMPins()
|
|
112
|
+
|
|
113
|
+
boxes = []
|
|
114
|
+
|
|
115
|
+
for i in range(len(mpins)):
|
|
116
|
+
mpin = mpins[i]
|
|
117
|
+
geometries = mpin.getGeometry()
|
|
118
|
+
for box in geometries:
|
|
119
|
+
# llx, lly = box.xMin(), box.yMin()
|
|
120
|
+
# urx, ury = box.xMax(), box.yMax()
|
|
121
|
+
ll = odb.Point(box.xMin(), box.yMin())
|
|
122
|
+
ur = odb.Point(box.xMax(), box.yMax())
|
|
123
|
+
transform.apply(ll)
|
|
124
|
+
transform.apply(ur)
|
|
125
|
+
layer = box.getTechLayer()
|
|
126
|
+
boxes.append((layer, ll, ur))
|
|
127
|
+
|
|
128
|
+
return boxes
|
|
129
|
+
|
|
130
|
+
def labelITerm(top, pad_iterm, pin_name, iotype, all_shapes_flag=False):
|
|
131
|
+
net_name = pin_name
|
|
132
|
+
net = top.block.findNet(net_name)
|
|
133
|
+
if net is None:
|
|
134
|
+
net = odb.dbNet_create(top.block, net_name)
|
|
135
|
+
|
|
136
|
+
pin_bterm = top.block.findBTerm(pin_name)
|
|
137
|
+
if pin_bterm is None:
|
|
138
|
+
pin_bterm = odb.dbBTerm_create(net, pin_name)
|
|
139
|
+
|
|
140
|
+
assert pin_bterm is not None, "Failed to create or find " + pin_name
|
|
141
|
+
|
|
142
|
+
pin_bterm.setIoType(iotype)
|
|
143
|
+
|
|
144
|
+
pin_bpin = odb.dbBPin_create(pin_bterm)
|
|
145
|
+
pin_bpin.setPlacementStatus("PLACED")
|
|
146
|
+
|
|
147
|
+
if not all_shapes_flag:
|
|
148
|
+
boxes = getBiggestBox(pad_iterm)
|
|
149
|
+
else:
|
|
150
|
+
boxes = getAllBoxes(pad_iterm)
|
|
151
|
+
|
|
152
|
+
for box in boxes:
|
|
153
|
+
layer, ll, ur = box
|
|
154
|
+
odb.dbBox_create(
|
|
155
|
+
pin_bpin, layer, ll.getX(), ll.getY(), ur.getX(), ur.getY()
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
pad_iterm.connect(net)
|
|
159
|
+
pin_bterm.connect(net)
|
|
160
|
+
|
|
161
|
+
mapping = OdbReader(input_lefs, netlist_def)
|
|
162
|
+
|
|
163
|
+
pad_pin_map = {}
|
|
164
|
+
for net in mapping.block.getNets():
|
|
165
|
+
iterms = net.getITerms()
|
|
166
|
+
bterms = net.getBTerms()
|
|
167
|
+
if len(iterms) >= 1 and len(bterms) == 1:
|
|
168
|
+
pin_name = bterms[0].getName()
|
|
169
|
+
if pin_name in extra_mappings_pin_names:
|
|
170
|
+
if verbose:
|
|
171
|
+
print(pin_name, "handled by an external mapping; skipping...")
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
pad_name = None
|
|
175
|
+
pad_pin_name = None
|
|
176
|
+
for iterm in iterms:
|
|
177
|
+
iterm_pin_name = iterm.getMTerm().getName()
|
|
178
|
+
if iterm_pin_name == pad_pin_name:
|
|
179
|
+
pad_name = iterm.getInst().getName()
|
|
180
|
+
pad_pin_name = iterm_pin_name
|
|
181
|
+
break
|
|
182
|
+
|
|
183
|
+
# '\[' and '\]' are common DEF names
|
|
184
|
+
|
|
185
|
+
if pad_name is None:
|
|
186
|
+
print(
|
|
187
|
+
"Warning: net",
|
|
188
|
+
net.getName(),
|
|
189
|
+
"has a BTerm but no ITerms that match PAD_PIN_NAME",
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
print("Warning: will label the first ITerm on the net!!!!!!!")
|
|
193
|
+
|
|
194
|
+
pad_name = iterms[0].getInst().getName()
|
|
195
|
+
pad_pin_name = iterms[0].getMTerm().getName()
|
|
196
|
+
|
|
197
|
+
if verbose:
|
|
198
|
+
print(
|
|
199
|
+
"Labeling ",
|
|
200
|
+
net.getName(),
|
|
201
|
+
"(",
|
|
202
|
+
pin_name,
|
|
203
|
+
"-",
|
|
204
|
+
pad_name,
|
|
205
|
+
"/",
|
|
206
|
+
pad_pin_name,
|
|
207
|
+
")",
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
pad_pin_map.setdefault(pad_name, [])
|
|
211
|
+
pad_pin_map[pad_name].append(
|
|
212
|
+
(pad_pin_name, pin_name, bterms[0].getIoType())
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
for mapping in extra_mappings:
|
|
216
|
+
pad_pin_map.setdefault(mapping[0], [])
|
|
217
|
+
pad_pin_map[mapping[0]].append((mapping[1], mapping[2], mapping[3]))
|
|
218
|
+
|
|
219
|
+
pad_pins_to_label_count = len(
|
|
220
|
+
[
|
|
221
|
+
mapping
|
|
222
|
+
for sublist in [pair[1] for pair in pad_pin_map.items()]
|
|
223
|
+
for mapping in sublist
|
|
224
|
+
]
|
|
225
|
+
)
|
|
226
|
+
bterms = mapping.block.getBTerms()
|
|
227
|
+
print(
|
|
228
|
+
set([bterm.getName() for bterm in bterms])
|
|
229
|
+
- set(
|
|
230
|
+
[
|
|
231
|
+
mapping[1]
|
|
232
|
+
for sublist in [pair[1] for pair in pad_pin_map.items()]
|
|
233
|
+
for mapping in sublist
|
|
234
|
+
]
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
assert pad_pins_to_label_count == len(
|
|
238
|
+
bterms
|
|
239
|
+
), "Some pins were not going to be labeled %d/%d" % (
|
|
240
|
+
pad_pins_to_label_count,
|
|
241
|
+
len(bterms),
|
|
242
|
+
)
|
|
243
|
+
print("Labeling", len(pad_pin_map), "pads")
|
|
244
|
+
print("Labeling", pad_pins_to_label_count, "pad pins")
|
|
245
|
+
if verbose:
|
|
246
|
+
print(pad_pin_map)
|
|
247
|
+
|
|
248
|
+
##############
|
|
249
|
+
|
|
250
|
+
labeled_count = 0
|
|
251
|
+
labeled = []
|
|
252
|
+
for inst in top.block.getInsts():
|
|
253
|
+
inst_name = inst.getName()
|
|
254
|
+
if inst_name in pad_pin_map:
|
|
255
|
+
for mapping in pad_pin_map[inst_name]:
|
|
256
|
+
labeled_count += 1
|
|
257
|
+
pad_pin_name = mapping[0]
|
|
258
|
+
pin_name = mapping[1]
|
|
259
|
+
iotype = mapping[2]
|
|
260
|
+
if verbose:
|
|
261
|
+
print("Found: ", inst_name, pad_pin_name, pin_name)
|
|
262
|
+
|
|
263
|
+
pad_iterm = inst.findITerm(pad_pin_name)
|
|
264
|
+
|
|
265
|
+
labelITerm(top, pad_iterm, pin_name, iotype, all_shapes_flag=all_shapes)
|
|
266
|
+
|
|
267
|
+
labeled.append(inst_name)
|
|
268
|
+
|
|
269
|
+
assert labeled_count == pad_pins_to_label_count, (
|
|
270
|
+
"Didn't label what I set out to label %d/%d"
|
|
271
|
+
% (labeled_count, pad_pins_to_label_count),
|
|
272
|
+
set(pad_pin_map.keys()) - set(labeled),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
if __name__ == "__main__":
|
|
277
|
+
label_macro_pins()
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Copyright 2020-2022 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
|
+
|
|
16
|
+
from reader import OdbReader, click
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@click.group()
|
|
20
|
+
def cli():
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@click.command("get_metal_layers")
|
|
25
|
+
@click.option("--output", "-o", default="/dev/stdout", help="Output file.")
|
|
26
|
+
@click.argument("lefs", nargs=-1)
|
|
27
|
+
def get_metal_layers(output, lefs):
|
|
28
|
+
reader = OdbReader(lefs, None)
|
|
29
|
+
|
|
30
|
+
layers = [
|
|
31
|
+
layer for layer in reader.tech.getLayers() if layer.getRoutingLevel() >= 1
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
layer_names = [
|
|
35
|
+
layer.getName()
|
|
36
|
+
for layer in sorted(layers, key=lambda layer: layer.getRoutingLevel())
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
with open(output, "w") as f:
|
|
40
|
+
f.write(" ".join(layer_names))
|
|
41
|
+
|
|
42
|
+
print(layer_names)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
cli.add_command(get_metal_layers)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@click.command("zeroize_origin")
|
|
49
|
+
@click.option("--output", "-o", default="./out.lef", help="Output file.")
|
|
50
|
+
@click.argument("lef")
|
|
51
|
+
def zeroize_origin(output, lef):
|
|
52
|
+
RECT_REGEX = re.compile(
|
|
53
|
+
r"^\s*RECT\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s+;$"
|
|
54
|
+
)
|
|
55
|
+
ORIGIN_REGEX = re.compile(r"^\s*ORIGIN\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s+;$")
|
|
56
|
+
|
|
57
|
+
lef_lines = open(lef).read().splitlines()
|
|
58
|
+
|
|
59
|
+
with open(output, "w") as f:
|
|
60
|
+
OFFSET_X = OFFSET_Y = 0
|
|
61
|
+
for line in lef_lines:
|
|
62
|
+
if line.isspace():
|
|
63
|
+
continue
|
|
64
|
+
origin_match = ORIGIN_REGEX.search(line)
|
|
65
|
+
if origin_match:
|
|
66
|
+
OFFSET_X, OFFSET_Y = float(origin_match.group(1)), float(
|
|
67
|
+
origin_match.group(2)
|
|
68
|
+
)
|
|
69
|
+
print(line[: line.find("O")] + "ORIGIN %.3f %.3f ;" % (0, 0), file=f)
|
|
70
|
+
else:
|
|
71
|
+
rect_match = RECT_REGEX.search(line)
|
|
72
|
+
if rect_match:
|
|
73
|
+
llx, lly, urx, ury = (
|
|
74
|
+
float(rect_match.group(1)),
|
|
75
|
+
float(rect_match.group(2)),
|
|
76
|
+
float(rect_match.group(3)),
|
|
77
|
+
float(rect_match.group(4)),
|
|
78
|
+
)
|
|
79
|
+
print(
|
|
80
|
+
line[: line.find("R")]
|
|
81
|
+
+ "RECT %.3f %.3f %.3f %.3f ;"
|
|
82
|
+
% (
|
|
83
|
+
llx + OFFSET_X,
|
|
84
|
+
lly + OFFSET_Y,
|
|
85
|
+
urx + OFFSET_X,
|
|
86
|
+
ury + OFFSET_Y,
|
|
87
|
+
),
|
|
88
|
+
file=f,
|
|
89
|
+
)
|
|
90
|
+
else:
|
|
91
|
+
print(line, end="", file=f)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
cli.add_command(zeroize_origin)
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
cli()
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright 2020 Efabless Corporation
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
import sys
|
|
16
|
+
from decimal import Decimal
|
|
17
|
+
|
|
18
|
+
from reader import click_odb, click
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@click.group
|
|
22
|
+
def cli():
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
LEF2OA_MAP = {
|
|
27
|
+
"N": "R0",
|
|
28
|
+
"S": "R180",
|
|
29
|
+
"W": "R90",
|
|
30
|
+
"E": "R270",
|
|
31
|
+
"FN": "MY",
|
|
32
|
+
"FS": "MX",
|
|
33
|
+
"FW": "MXR90",
|
|
34
|
+
"FE": "MYR90",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def lef_rot_to_oa_rot(rot):
|
|
39
|
+
if rot in LEF2OA_MAP:
|
|
40
|
+
return LEF2OA_MAP[rot]
|
|
41
|
+
else:
|
|
42
|
+
assert rot in [item[1] for item in LEF2OA_MAP.items()], rot
|
|
43
|
+
return rot
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def gridify(n, f):
|
|
47
|
+
"""
|
|
48
|
+
e.g., (1.1243, 0.005) -> 1.120
|
|
49
|
+
"""
|
|
50
|
+
return round(n / f) * f
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@click.command()
|
|
54
|
+
@click.option("-c", "--config", required=True, help="Configuration file")
|
|
55
|
+
@click.option(
|
|
56
|
+
"-f",
|
|
57
|
+
"--fixed",
|
|
58
|
+
default=False,
|
|
59
|
+
is_flag=True,
|
|
60
|
+
help="A flag to signal whether the placement should be fixed or not",
|
|
61
|
+
)
|
|
62
|
+
@click_odb
|
|
63
|
+
def manual_macro_placement(reader, config, fixed):
|
|
64
|
+
"""
|
|
65
|
+
Places macros in positions and orientations specified by a config file
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
db_units_per_micron = reader.block.getDbUnitsPerMicron()
|
|
69
|
+
|
|
70
|
+
# read config
|
|
71
|
+
macros = {}
|
|
72
|
+
with open(config, "r") as config_file:
|
|
73
|
+
for line in config_file:
|
|
74
|
+
# Discard comments and empty lines
|
|
75
|
+
line = line.split("#")[0].strip()
|
|
76
|
+
if not line:
|
|
77
|
+
continue
|
|
78
|
+
line = line.split()
|
|
79
|
+
name, x, y, orientation = line
|
|
80
|
+
macro_data = [
|
|
81
|
+
name,
|
|
82
|
+
int(Decimal(x) * db_units_per_micron),
|
|
83
|
+
int(Decimal(y) * db_units_per_micron),
|
|
84
|
+
orientation,
|
|
85
|
+
]
|
|
86
|
+
name_escaped = reader.escape_verilog_name(name)
|
|
87
|
+
macros[name_escaped] = macro_data
|
|
88
|
+
|
|
89
|
+
print("Placing the following macros:")
|
|
90
|
+
print(macros)
|
|
91
|
+
|
|
92
|
+
print("Design name:", reader.name)
|
|
93
|
+
|
|
94
|
+
macros_cnt = len(macros)
|
|
95
|
+
for inst in reader.block.getInsts():
|
|
96
|
+
inst_name = inst.getName()
|
|
97
|
+
if inst.isFixed():
|
|
98
|
+
assert inst_name not in macros, inst_name
|
|
99
|
+
continue
|
|
100
|
+
if inst_name in macros:
|
|
101
|
+
print("Placing", inst_name)
|
|
102
|
+
macro_data = macros[inst_name]
|
|
103
|
+
_, x, y, orientation = macro_data
|
|
104
|
+
x = gridify(x, 5)
|
|
105
|
+
y = gridify(y, 5)
|
|
106
|
+
inst.setOrient(lef_rot_to_oa_rot(orientation))
|
|
107
|
+
inst.setLocation(x, y)
|
|
108
|
+
if fixed:
|
|
109
|
+
inst.setPlacementStatus("FIRM")
|
|
110
|
+
else:
|
|
111
|
+
inst.setPlacementStatus("PLACED")
|
|
112
|
+
del macros[inst_name]
|
|
113
|
+
|
|
114
|
+
if len(macros):
|
|
115
|
+
print("Declared macros not instantiated in design:", file=sys.stderr)
|
|
116
|
+
for macro in macros.values():
|
|
117
|
+
print(f"* {macro[0]}", file=sys.stderr)
|
|
118
|
+
exit(1)
|
|
119
|
+
|
|
120
|
+
print(f"Successfully placed {macros_cnt} instances.")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
cli.add_command(manual_macro_placement)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@click.command()
|
|
127
|
+
@click_odb
|
|
128
|
+
def manual_global_placement(reader):
|
|
129
|
+
db_units_per_micron = reader.block.getDbUnitsPerMicron()
|
|
130
|
+
|
|
131
|
+
data = reader.config["MANUAL_GLOBAL_PLACEMENTS"]
|
|
132
|
+
not_found = []
|
|
133
|
+
for instance, info in data.items():
|
|
134
|
+
name_escaped = reader.escape_verilog_name(instance)
|
|
135
|
+
x, y = info["location"]
|
|
136
|
+
orientation = lef_rot_to_oa_rot(info["orientation"])
|
|
137
|
+
found = False
|
|
138
|
+
for inst in reader.block.getInsts():
|
|
139
|
+
if inst.getName() == name_escaped:
|
|
140
|
+
found = True
|
|
141
|
+
x_dbu = int(x * db_units_per_micron)
|
|
142
|
+
y_dbu = int(y * db_units_per_micron)
|
|
143
|
+
inst.setOrient(lef_rot_to_oa_rot(orientation))
|
|
144
|
+
inst.setLocation(x_dbu, y_dbu)
|
|
145
|
+
inst.setPlacementStatus("PLACED")
|
|
146
|
+
break
|
|
147
|
+
if not found:
|
|
148
|
+
not_found.append(instance)
|
|
149
|
+
|
|
150
|
+
if len(not_found):
|
|
151
|
+
print(
|
|
152
|
+
"[ERROR] One or more instances not found. Make sure you use their Verilog and not their LEF names."
|
|
153
|
+
)
|
|
154
|
+
for instance in not_found:
|
|
155
|
+
print(f"* {instance}")
|
|
156
|
+
exit(1)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
cli.add_command(manual_global_placement)
|
|
160
|
+
|
|
161
|
+
if __name__ == "__main__":
|
|
162
|
+
cli()
|