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.

Files changed (170) hide show
  1. librelane/__init__.py +38 -0
  2. librelane/__main__.py +479 -0
  3. librelane/__version__.py +43 -0
  4. librelane/common/__init__.py +63 -0
  5. librelane/common/cli.py +75 -0
  6. librelane/common/drc.py +246 -0
  7. librelane/common/generic_dict.py +319 -0
  8. librelane/common/metrics/__init__.py +35 -0
  9. librelane/common/metrics/__main__.py +413 -0
  10. librelane/common/metrics/library.py +354 -0
  11. librelane/common/metrics/metric.py +186 -0
  12. librelane/common/metrics/util.py +279 -0
  13. librelane/common/misc.py +456 -0
  14. librelane/common/ring_buffer.py +63 -0
  15. librelane/common/tcl.py +80 -0
  16. librelane/common/toolbox.py +549 -0
  17. librelane/common/tpe.py +41 -0
  18. librelane/common/types.py +116 -0
  19. librelane/config/__init__.py +32 -0
  20. librelane/config/__main__.py +155 -0
  21. librelane/config/config.py +1025 -0
  22. librelane/config/flow.py +490 -0
  23. librelane/config/pdk_compat.py +255 -0
  24. librelane/config/preprocessor.py +464 -0
  25. librelane/config/removals.py +45 -0
  26. librelane/config/variable.py +743 -0
  27. librelane/container.py +285 -0
  28. librelane/env_info.py +320 -0
  29. librelane/examples/spm/config.yaml +33 -0
  30. librelane/examples/spm/pin_order.cfg +14 -0
  31. librelane/examples/spm/src/impl.sdc +73 -0
  32. librelane/examples/spm/src/signoff.sdc +68 -0
  33. librelane/examples/spm/src/spm.v +73 -0
  34. librelane/examples/spm/verify/spm_tb.v +106 -0
  35. librelane/examples/spm-user_project_wrapper/SPM_example.v +286 -0
  36. librelane/examples/spm-user_project_wrapper/base_sdc_file.sdc +145 -0
  37. librelane/examples/spm-user_project_wrapper/config-tut.json +12 -0
  38. librelane/examples/spm-user_project_wrapper/config.json +13 -0
  39. librelane/examples/spm-user_project_wrapper/defines.v +66 -0
  40. librelane/examples/spm-user_project_wrapper/template.def +7656 -0
  41. librelane/examples/spm-user_project_wrapper/user_project_wrapper.v +123 -0
  42. librelane/flows/__init__.py +24 -0
  43. librelane/flows/builtins.py +18 -0
  44. librelane/flows/classic.py +327 -0
  45. librelane/flows/cli.py +463 -0
  46. librelane/flows/flow.py +1049 -0
  47. librelane/flows/misc.py +71 -0
  48. librelane/flows/optimizing.py +179 -0
  49. librelane/flows/sequential.py +367 -0
  50. librelane/flows/synth_explore.py +173 -0
  51. librelane/help/__main__.py +39 -0
  52. librelane/logging/__init__.py +40 -0
  53. librelane/logging/logger.py +323 -0
  54. librelane/open_pdks_rev +1 -0
  55. librelane/plugins.py +21 -0
  56. librelane/py.typed +0 -0
  57. librelane/scripts/base.sdc +80 -0
  58. librelane/scripts/klayout/Readme.md +2 -0
  59. librelane/scripts/klayout/open_design.py +63 -0
  60. librelane/scripts/klayout/render.py +121 -0
  61. librelane/scripts/klayout/stream_out.py +176 -0
  62. librelane/scripts/klayout/xml_drc_report_to_json.py +45 -0
  63. librelane/scripts/klayout/xor.drc +120 -0
  64. librelane/scripts/magic/Readme.md +1 -0
  65. librelane/scripts/magic/common/read.tcl +114 -0
  66. librelane/scripts/magic/def/antenna_check.tcl +35 -0
  67. librelane/scripts/magic/def/mag.tcl +19 -0
  68. librelane/scripts/magic/def/mag_gds.tcl +79 -0
  69. librelane/scripts/magic/drc.tcl +78 -0
  70. librelane/scripts/magic/extract_spice.tcl +98 -0
  71. librelane/scripts/magic/gds/drc_batch.tcl +74 -0
  72. librelane/scripts/magic/gds/erase_box.tcl +32 -0
  73. librelane/scripts/magic/gds/extras_mag.tcl +45 -0
  74. librelane/scripts/magic/gds/mag_with_pointers.tcl +31 -0
  75. librelane/scripts/magic/get_bbox.tcl +11 -0
  76. librelane/scripts/magic/lef/extras_maglef.tcl +61 -0
  77. librelane/scripts/magic/lef/maglef.tcl +26 -0
  78. librelane/scripts/magic/lef.tcl +57 -0
  79. librelane/scripts/magic/open.tcl +28 -0
  80. librelane/scripts/magic/wrapper.tcl +21 -0
  81. librelane/scripts/netgen/setup.tcl +28 -0
  82. librelane/scripts/odbpy/apply_def_template.py +49 -0
  83. librelane/scripts/odbpy/cell_frequency.py +107 -0
  84. librelane/scripts/odbpy/check_antenna_properties.py +116 -0
  85. librelane/scripts/odbpy/contextualize.py +109 -0
  86. librelane/scripts/odbpy/defutil.py +573 -0
  87. librelane/scripts/odbpy/diodes.py +373 -0
  88. librelane/scripts/odbpy/disconnected_pins.py +305 -0
  89. librelane/scripts/odbpy/eco_buffer.py +181 -0
  90. librelane/scripts/odbpy/eco_diode.py +139 -0
  91. librelane/scripts/odbpy/filter_unannotated.py +100 -0
  92. librelane/scripts/odbpy/io_place.py +482 -0
  93. librelane/scripts/odbpy/ioplace_parser/__init__.py +23 -0
  94. librelane/scripts/odbpy/ioplace_parser/parse.py +147 -0
  95. librelane/scripts/odbpy/label_macro_pins.py +277 -0
  96. librelane/scripts/odbpy/lefutil.py +97 -0
  97. librelane/scripts/odbpy/placers.py +162 -0
  98. librelane/scripts/odbpy/power_utils.py +397 -0
  99. librelane/scripts/odbpy/random_place.py +57 -0
  100. librelane/scripts/odbpy/reader.py +250 -0
  101. librelane/scripts/odbpy/remove_buffers.py +173 -0
  102. librelane/scripts/odbpy/snap_to_grid.py +57 -0
  103. librelane/scripts/odbpy/wire_lengths.py +93 -0
  104. librelane/scripts/openroad/antenna_check.tcl +20 -0
  105. librelane/scripts/openroad/antenna_repair.tcl +31 -0
  106. librelane/scripts/openroad/basic_mp.tcl +24 -0
  107. librelane/scripts/openroad/buffer_list.tcl +10 -0
  108. librelane/scripts/openroad/common/dpl.tcl +24 -0
  109. librelane/scripts/openroad/common/dpl_cell_pad.tcl +26 -0
  110. librelane/scripts/openroad/common/grt.tcl +32 -0
  111. librelane/scripts/openroad/common/io.tcl +540 -0
  112. librelane/scripts/openroad/common/pdn_cfg.tcl +135 -0
  113. librelane/scripts/openroad/common/resizer.tcl +103 -0
  114. librelane/scripts/openroad/common/set_global_connections.tcl +78 -0
  115. librelane/scripts/openroad/common/set_layer_adjustments.tcl +31 -0
  116. librelane/scripts/openroad/common/set_power_nets.tcl +30 -0
  117. librelane/scripts/openroad/common/set_rc.tcl +75 -0
  118. librelane/scripts/openroad/common/set_routing_layers.tcl +30 -0
  119. librelane/scripts/openroad/cts.tcl +80 -0
  120. librelane/scripts/openroad/cut_rows.tcl +24 -0
  121. librelane/scripts/openroad/dpl.tcl +24 -0
  122. librelane/scripts/openroad/drt.tcl +37 -0
  123. librelane/scripts/openroad/fill.tcl +30 -0
  124. librelane/scripts/openroad/floorplan.tcl +145 -0
  125. librelane/scripts/openroad/gpl.tcl +88 -0
  126. librelane/scripts/openroad/grt.tcl +30 -0
  127. librelane/scripts/openroad/gui.tcl +37 -0
  128. librelane/scripts/openroad/insert_buffer.tcl +127 -0
  129. librelane/scripts/openroad/ioplacer.tcl +67 -0
  130. librelane/scripts/openroad/irdrop.tcl +51 -0
  131. librelane/scripts/openroad/pdn.tcl +52 -0
  132. librelane/scripts/openroad/rcx.tcl +32 -0
  133. librelane/scripts/openroad/repair_design.tcl +70 -0
  134. librelane/scripts/openroad/repair_design_postgrt.tcl +48 -0
  135. librelane/scripts/openroad/rsz_timing_postcts.tcl +68 -0
  136. librelane/scripts/openroad/rsz_timing_postgrt.tcl +70 -0
  137. librelane/scripts/openroad/sta/check_macro_instances.tcl +53 -0
  138. librelane/scripts/openroad/sta/corner.tcl +393 -0
  139. librelane/scripts/openroad/tapcell.tcl +25 -0
  140. librelane/scripts/openroad/write_views.tcl +27 -0
  141. librelane/scripts/pyosys/construct_abc_script.py +177 -0
  142. librelane/scripts/pyosys/json_header.py +84 -0
  143. librelane/scripts/pyosys/synthesize.py +493 -0
  144. librelane/scripts/pyosys/ys_common.py +153 -0
  145. librelane/scripts/tclsh/hello.tcl +1 -0
  146. librelane/state/__init__.py +24 -0
  147. librelane/state/__main__.py +61 -0
  148. librelane/state/design_format.py +195 -0
  149. librelane/state/state.py +359 -0
  150. librelane/steps/__init__.py +61 -0
  151. librelane/steps/__main__.py +510 -0
  152. librelane/steps/checker.py +637 -0
  153. librelane/steps/common_variables.py +340 -0
  154. librelane/steps/cvc_rv.py +169 -0
  155. librelane/steps/klayout.py +509 -0
  156. librelane/steps/magic.py +576 -0
  157. librelane/steps/misc.py +160 -0
  158. librelane/steps/netgen.py +253 -0
  159. librelane/steps/odb.py +1088 -0
  160. librelane/steps/openroad.py +2460 -0
  161. librelane/steps/openroad_alerts.py +102 -0
  162. librelane/steps/pyosys.py +640 -0
  163. librelane/steps/step.py +1571 -0
  164. librelane/steps/tclstep.py +288 -0
  165. librelane/steps/verilator.py +222 -0
  166. librelane/steps/yosys.py +371 -0
  167. librelane-2.4.0.dist-info/METADATA +169 -0
  168. librelane-2.4.0.dist-info/RECORD +170 -0
  169. librelane-2.4.0.dist-info/WHEEL +4 -0
  170. librelane-2.4.0.dist-info/entry_points.txt +9 -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()