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,397 @@
1
+ #!/usr/bin/env python3
2
+ # Copyright 2020-2022 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
+ import utl
17
+
18
+ import re
19
+ import json
20
+ from dataclasses import dataclass
21
+ from typing import Dict, List, Optional
22
+
23
+ from reader import OdbReader, click_odb, click
24
+
25
+
26
+ @click.group()
27
+ def cli():
28
+ pass
29
+
30
+
31
+ class Design(object):
32
+ @dataclass
33
+ class Instance:
34
+ name: str
35
+ module_name: str
36
+ power_connections: Dict[str, str]
37
+ ground_connections: Dict[str, str]
38
+
39
+ def __init__(self, reader: OdbReader, yosys_dict: dict) -> None:
40
+ self.reader = reader
41
+ self.design_name = reader.block.getName()
42
+ self.yosys_dict = yosys_dict
43
+
44
+ self.pins_by_module_name: Dict[str, Dict[str, odb.dbMTerm]] = {}
45
+ self.verilog_net_names_by_bit_by_module: Dict[str, Dict[int, str]] = {}
46
+ self.nets_by_net_name = {net.getName(): net for net in reader.block.getNets()}
47
+
48
+ def get_verilog_net_name_by_bit(self, top_module: str, target_bit: int):
49
+ yosys_design_object = self.yosys_dict["modules"][top_module]
50
+ if top_module not in self.verilog_net_names_by_bit_by_module:
51
+ # check git history for a version of this loop that is drunk on power
52
+ netname_by_bit = {}
53
+
54
+ for netname, info in yosys_design_object["netnames"].items():
55
+ for bit in info["bits"]:
56
+ netname_by_bit[bit] = netname
57
+
58
+ self.verilog_net_names_by_bit_by_module[top_module] = netname_by_bit
59
+ return self.verilog_net_names_by_bit_by_module[top_module][target_bit]
60
+
61
+ def get_pins(self, module_name: str) -> Dict[str, odb.dbMTerm]:
62
+ if module_name not in self.pins_by_module_name:
63
+ master = self.reader.db.findMaster(module_name)
64
+ if master is None:
65
+ print(
66
+ f"[ERROR] No LEF view for {module_name} was found in the database even though it is declared in the Verilog."
67
+ )
68
+ exit(-1)
69
+ pins = {pin.getName(): pin for pin in master.getMTerms()}
70
+ self.pins_by_module_name[module_name] = pins
71
+ return self.pins_by_module_name[module_name]
72
+
73
+ def is_power(self, module_name: str, pin_name: str) -> bool:
74
+ module_pins = self.get_pins(module_name)
75
+ if pin_name not in module_pins:
76
+ print(
77
+ f"[ERROR] No pin {pin_name} found in the module {module_name}'s LEF view: the LEF and Verilog views of the module may be mismatched."
78
+ )
79
+ exit(-1)
80
+
81
+ return module_pins[pin_name].getSigType() == "POWER"
82
+
83
+ def is_ground(self, module_name: str, pin_name: str) -> bool:
84
+ module_pins = self.get_pins(module_name)
85
+ if pin_name not in module_pins:
86
+ print(
87
+ f"[ERROR] No pin {pin_name} found in the module {module_name}'s LEF view: the LEF and Verilog views of the module may be mismatched."
88
+ )
89
+ exit(-1)
90
+
91
+ return module_pins[pin_name].getSigType() == "GROUND"
92
+
93
+ def extract_pg_pins(self, top_module: str, cell_name: str) -> dict:
94
+ yosys_design_object = self.yosys_dict["modules"][top_module]
95
+ cells = yosys_design_object["cells"]
96
+ module_name = cells[cell_name]["type"]
97
+ master = self.reader.db.findMaster(module_name)
98
+ if master is None:
99
+ print(
100
+ f"[ERROR] Could not find master for cell type '{module_name}' in the database."
101
+ )
102
+ exit(-1)
103
+
104
+ lef_pg_pins = []
105
+ for pin in master.getMTerms():
106
+ if pin.getSigType() in ["POWER", "GROUND"]:
107
+ lef_pg_pins.append((pin.getName(), pin.getSigType()))
108
+
109
+ power_pins = {}
110
+ ground_pins = {}
111
+ connections = cells[cell_name]["connections"]
112
+ for pin_name, sigtype in lef_pg_pins:
113
+ if pin_name not in connections:
114
+ # Bad Verilog view-- error would break backcompat
115
+ continue
116
+ connection_bits = connections[pin_name]
117
+ if len(connection_bits) != 1:
118
+ print(
119
+ f"[ERROR] Unexpectedly found more than one bit connected to {sigtype} pin {module_name}/{pin_name}."
120
+ )
121
+ exit(-1)
122
+ connection_bit = connection_bits[0]
123
+ connected_to_v = self.get_verilog_net_name_by_bit(
124
+ top_module,
125
+ connection_bit,
126
+ )
127
+ (power_pins if sigtype == "POWER" else ground_pins)[
128
+ pin_name
129
+ ] = connected_to_v
130
+
131
+ return power_pins, ground_pins
132
+
133
+ def extract_instances(
134
+ self,
135
+ top_module: str,
136
+ prefix: str = "",
137
+ ) -> List["Design.Instance"]:
138
+ yosys_design_object = self.yosys_dict["modules"][top_module]
139
+ instances = []
140
+ cells = yosys_design_object["cells"]
141
+ for cell_name in cells.keys():
142
+ module_name = cells[cell_name]["type"]
143
+ if module_name.startswith("$"):
144
+ # yosys primitive
145
+ continue
146
+ if module_name in self.yosys_dict["modules"]:
147
+ print(
148
+ f"[WARNING] Macros inside hierarchical netlists are not currently supported in LibreLane: skipping submodule '{cell_name}' of type '{module_name}'."
149
+ )
150
+ continue
151
+ # sub_instances = self.extract_instances(
152
+ # self.yosys_dict["modules"][module_name],
153
+ # prefix=prefix + f"{cell_name}/",
154
+ # )
155
+ # instances += sub_instances
156
+ power_pins, ground_pins = self.extract_pg_pins(
157
+ top_module,
158
+ cell_name,
159
+ )
160
+ instances.append(
161
+ Design.Instance(
162
+ name=prefix + cell_name,
163
+ ground_connections=ground_pins,
164
+ power_connections=power_pins,
165
+ module_name=module_name,
166
+ )
167
+ )
168
+
169
+ return instances
170
+
171
+ def add_global_connection(
172
+ self,
173
+ net_name: str,
174
+ inst_name: str,
175
+ pin_name: str,
176
+ power: bool = False,
177
+ ground: bool = False,
178
+ region: Optional[odb.dbRegion] = None,
179
+ ):
180
+ # Function adapted from OpenROAD
181
+ #
182
+ # Copyright (c) 2022, The Regents of the University of California
183
+ # All rights reserved.
184
+ #
185
+ # Redistribution and use in source and binary forms, with or without
186
+ # modification, are permitted provided that the following conditions are met:
187
+ #
188
+ # * Redistributions of source code must retain the above copyright notice, this
189
+ # list of conditions and the following disclaimer.
190
+ #
191
+ # * Redistributions in binary form must reproduce the above copyright notice,
192
+ # this list of conditions and the following disclaimer in the documentation
193
+ # and/or other materials provided with the distribution.
194
+ #
195
+ # * Neither the name of the copyright holder nor the names of its
196
+ # contributors may be used to endorse or promote products derived from
197
+ # this software without specific prior written permission.
198
+ #
199
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
200
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
201
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
202
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
203
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
204
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
205
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
206
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
207
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
208
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
209
+ # POSSIBILITY OF SUCH DAMAGE.
210
+
211
+ design = self.reader.chip
212
+ net = design.getBlock().findNet(net_name)
213
+ if net is None:
214
+ net = odb.dbNet_create(design.getBlock(), net_name)
215
+
216
+ if power and ground:
217
+ utl.error(utl.PDN, 1551, "Only power or ground can be specified")
218
+ elif power:
219
+ net.setSpecial()
220
+ net.setSigType("POWER")
221
+ elif ground:
222
+ net.setSpecial()
223
+ net.setSigType("GROUND")
224
+
225
+ if region is not None:
226
+ region = design.getBlock().findRegion(region)
227
+ if region is None:
228
+ utl.error(utl.PDN, 1504, f"Region {region} not defined")
229
+ exit(-1)
230
+
231
+ inst_def_name = self.reader.escape_verilog_name(inst_name)
232
+ pin_def_name = self.reader.escape_verilog_name(pin_name)
233
+
234
+ for term in net.getITerms():
235
+ if (
236
+ term.getInst().getName() == inst_def_name
237
+ and term.getMTerm().getName() == pin_def_name
238
+ ):
239
+ print(
240
+ f"[INFO] {inst_name}/{pin_name} is already connected to {net.getName()} in the layout."
241
+ )
242
+ return
243
+
244
+ connected_items = design.getBlock().addGlobalConnect(
245
+ region,
246
+ re.escape(inst_def_name),
247
+ re.escape(pin_def_name),
248
+ net,
249
+ True,
250
+ )
251
+
252
+ print(f"[INFO] Made {connected_items} connections.")
253
+ if connected_items == 0:
254
+ print(
255
+ f"[ERROR] add_global_connections failed to make any connections for '{inst_name}/{pin_name}' to {net_name}."
256
+ )
257
+ exit(-1)
258
+ elif connected_items > 1:
259
+ print(
260
+ f"[ERROR] add_global_connections somehow made {connected_items} connections for '{inst_name}/{pin_name}' to {net_name} -- please report this as a bug"
261
+ )
262
+ exit(-1)
263
+
264
+
265
+ @click.command()
266
+ @click.option(
267
+ "--input-json",
268
+ type=click.Path(
269
+ exists=True,
270
+ file_okay=True,
271
+ dir_okay=False,
272
+ readable=True,
273
+ ),
274
+ required=True,
275
+ )
276
+ @click_odb
277
+ def set_power_connections(input_json, reader: OdbReader):
278
+ design_str = open(input_json).read()
279
+ yosys_dict = json.loads(design_str)
280
+
281
+ design = Design(reader, yosys_dict)
282
+ macro_instances = design.extract_instances(design.design_name)
283
+ for instance in macro_instances:
284
+ for pin in instance.power_connections.keys():
285
+ net_name = instance.power_connections[pin]
286
+ print(f"Connecting power net {net_name} to {instance.name}/{pin}…")
287
+ design.add_global_connection(
288
+ inst_name=instance.name,
289
+ net_name=net_name,
290
+ pin_name=pin,
291
+ power=True,
292
+ )
293
+ for pin in instance.ground_connections.keys():
294
+ net_name = instance.ground_connections[pin]
295
+ print(f"Connecting ground net {net_name} to {instance.name}/{pin}…")
296
+ design.add_global_connection(
297
+ inst_name=instance.name,
298
+ net_name=net_name,
299
+ pin_name=pin,
300
+ ground=True,
301
+ )
302
+
303
+
304
+ cli.add_command(set_power_connections)
305
+
306
+
307
+ @click.command()
308
+ @click.option(
309
+ "--output-vh",
310
+ "output_vh",
311
+ type=click.Path(exists=False, dir_okay=False, writable=True),
312
+ required=True,
313
+ )
314
+ @click.option(
315
+ "--input-json",
316
+ type=click.Path(
317
+ exists=True,
318
+ file_okay=True,
319
+ dir_okay=False,
320
+ readable=True,
321
+ ),
322
+ required=True,
323
+ )
324
+ @click.option(
325
+ "--power-define",
326
+ type=str,
327
+ required=False,
328
+ )
329
+ @click_odb
330
+ def write_verilog_header(
331
+ output_vh: str,
332
+ input_json: str,
333
+ power_define: Optional[str],
334
+ reader: OdbReader,
335
+ ):
336
+ input_dict = json.load(open(input_json))
337
+ design_name = reader.block.getName()
338
+ pg_bterms = {}
339
+ for bterm in reader.block.getBTerms():
340
+ name, sigtype = bterm.getName(), bterm.getSigType()
341
+ if sigtype in ["POWER", "GROUND"]:
342
+ pg_bterms[name] = sigtype
343
+
344
+ design_dict = input_dict["modules"][design_name]
345
+ ports = design_dict["ports"]
346
+ with open(output_vh, "w") as f:
347
+ # For power/ground pins, we rely primarily on the information from the
348
+ # layout as the user may not have defined them in Verilog at all
349
+ pg_decls = []
350
+ for name in pg_bterms:
351
+ direction = "inout"
352
+ if verilog_info := ports.get(name):
353
+ direction = verilog_info["direction"]
354
+ pg_decls.append(f"{direction} {name}")
355
+
356
+ # For signals, we rely on the information from the Verilog-generated
357
+ # header as the layout separates buses into individual pins
358
+ signal_decls = []
359
+ for name, info in ports.items():
360
+ if name in pg_bterms:
361
+ continue
362
+ bus_postfix = ""
363
+ # See https://github.com/YosysHQ/yosys/blob/91685355a082f1b5fbc539d0ec484f4d484f5baa/passes/cmds/portlist.cc#L65
364
+ upto = info.get("upto", 0) == 1
365
+ offset = info.get("offset", 0)
366
+ width = len(info["bits"])
367
+ if width > 1:
368
+ msb = offset + width - 1
369
+ lsb = offset
370
+ if upto:
371
+ msb, lsb = lsb, msb
372
+ bus_postfix = f"[{msb}:{lsb}]"
373
+ signal_decls.append(f"{info['direction']}{bus_postfix} {name}")
374
+
375
+ # Write module
376
+ print("// Auto-generated by LibreLane", file=f)
377
+ print(f"module {design_name}(", file=f)
378
+ last_pos = f.tell()
379
+ if power_define is not None:
380
+ print(f"`ifdef {power_define}", file=f)
381
+ for decl in pg_decls:
382
+ print(f" {decl}", file=f, end="")
383
+ last_pos = f.tell()
384
+ print(",", file=f)
385
+ print("`endif", file=f)
386
+ for decl in signal_decls:
387
+ print(f" {decl}", file=f, end="")
388
+ last_pos = f.tell()
389
+ print(",", file=f)
390
+ f.seek(last_pos) # Overwrite ,\n
391
+ print("\n);\nendmodule", file=f)
392
+
393
+
394
+ cli.add_command(write_verilog_header)
395
+
396
+ if __name__ == "__main__":
397
+ cli()
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env python3
2
+ # Copyright 2020-2022 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 random
16
+
17
+ from reader import click_odb, click
18
+
19
+
20
+ def gridify(n, f):
21
+ """
22
+ e.g., (1.1243, 0.005) -> 1.120
23
+ """
24
+ return round(n / f) * f
25
+
26
+
27
+ @click.command()
28
+ @click_odb
29
+ def random_place(reader):
30
+ core_area = reader.block.getCoreArea()
31
+ LLX, LLY = core_area.ll()
32
+ URX, URY = core_area.ur()
33
+ insts = reader.block.getInsts()
34
+
35
+ print("Design name:", reader.name)
36
+ print("Core Area Boundaries:", LLX, LLY, URX, URY)
37
+ print("Number of instances", len(insts))
38
+
39
+ placed_cnt = 0
40
+ for inst in insts:
41
+ if inst.isFixed():
42
+ continue
43
+ master = inst.getMaster()
44
+ master_width = master.getWidth()
45
+ master_height = master.getHeight()
46
+ x = gridify(random.randint(LLX, max(LLX, URX - master_width)), 5)
47
+ y = gridify(random.randint(LLY, max(LLY, URY - master_height)), 5)
48
+ inst.setLocation(x, y)
49
+ inst.setPlacementStatus("PLACED")
50
+
51
+ placed_cnt += 1
52
+
53
+ print(f"Placed {placed_cnt} instances.")
54
+
55
+
56
+ if __name__ == "__main__":
57
+ random_place()
@@ -0,0 +1,250 @@
1
+ # Copyright 2021-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
+ # flake8: noqa E402
15
+ import odb
16
+ from openroad import Tech, Design
17
+
18
+ import re
19
+ import sys
20
+ import json
21
+ import locale
22
+ import inspect
23
+ from functools import wraps
24
+ from decimal import Decimal
25
+ from fnmatch import fnmatch
26
+ from typing import Callable, Dict
27
+
28
+ # -- START: Environment Fixes
29
+ try:
30
+ locale.setlocale(locale.LC_ALL, "C.UTF-8")
31
+ except locale.Error:
32
+ # We tried. :)
33
+ pass
34
+ # -- END
35
+
36
+ import click
37
+ import rich
38
+ from rich.table import Table
39
+
40
+ # Re-export for subfunctions
41
+ rich
42
+ click
43
+ Table
44
+ odb
45
+
46
+ write_fn: Dict[str, Callable] = {
47
+ "def": lambda reader, file: file and reader.design.writeDef(file),
48
+ "odb": lambda reader, file: file and reader.design.writeDb(file),
49
+ }
50
+ auto_handled_output_opts = [f"output_{key}" for key in write_fn]
51
+
52
+
53
+ class OdbReader(object):
54
+ def __init__(self, *args, **kwargs):
55
+ self.ord_tech = Tech()
56
+ self.design = Design(self.ord_tech)
57
+
58
+ if len(args) == 1:
59
+ db_in = args[0]
60
+ self.design.readDb(db_in)
61
+ elif len(args) == 2:
62
+ lef_in, def_in = args
63
+ if not (isinstance(lef_in, list) or isinstance(lef_in, tuple)):
64
+ lef_in = [lef_in]
65
+ for lef in lef_in:
66
+ self.ord_tech.readLef(lef)
67
+ if def_in is not None:
68
+ self.design.readDef(def_in)
69
+
70
+ self.config = None
71
+ if "config_path" in kwargs and kwargs["config_path"] is not None:
72
+ self.config = json.load(
73
+ open(kwargs["config_path"], encoding="utf8"),
74
+ parse_float=Decimal,
75
+ )
76
+
77
+ self.db = self.ord_tech.getDB()
78
+ self.tech = self.db.getTech()
79
+ self.chip = self.db.getChip()
80
+ self.layers = {l.getName(): l for l in self.tech.getLayers()}
81
+ self.libs = self.db.getLibs()
82
+ self.cells = {}
83
+ for lib in self.libs:
84
+ self.cells.update({m: m for m in lib.getMasters()})
85
+ if self.chip is not None:
86
+ self.block = self.db.getChip().getBlock()
87
+ self.name = self.block.getName()
88
+ self.rows = self.block.getRows()
89
+ self.dbunits = self.block.getDefUnits()
90
+ self.instances = self.block.getInsts()
91
+
92
+ busbitchars = re.escape("[]") # TODO: Get alternatives from LEF parser
93
+ # dividerchar = re.escape("/") # TODO: Get alternatives from LEF parser
94
+ self.escape_verilog_rx = re.compile(rf"([{busbitchars}])")
95
+
96
+ def add_lef(self, new_lef):
97
+ self.ord_tech.readLef(new_lef)
98
+
99
+ def escape_verilog_name(self, name_in: str) -> str:
100
+ return self.escape_verilog_rx.sub(r"\\\1", name_in)
101
+
102
+ def _dpl(self):
103
+ """
104
+ The ``._dpl()`` method is EXPERIMENTAL and SHOULD NOT BE USED YET.
105
+
106
+ Use a composite step with ``OpenROAD.DetailedPlacement``.
107
+ """
108
+ if self.config is None:
109
+ raise RuntimeError("Attempted to call dpl without config file")
110
+
111
+ cell_pad_value = int(self.config["DPL_CELL_PADDING"])
112
+ cell_pad_side = cell_pad_value // 2
113
+ dpl = self.design.getOpendp()
114
+ dpl.setPaddingGlobal(cell_pad_side, cell_pad_side)
115
+ if wildcards := self.config["CELL_PAD_EXCLUDE"]:
116
+ for wildcard in wildcards:
117
+ masters = [
118
+ self.cells[name] for name in self.cells if fnmatch(name, wildcard)
119
+ ]
120
+ for master in masters:
121
+ dpl.setPadding(master, 0, 0)
122
+ if diode_padding := self.config["DIODE_PADDING"]:
123
+ name, _ = self.config["DIODE_CELL"].split("/")
124
+ master = self.cells[name]
125
+ dpl.setPadding(master, int(diode_padding), 0)
126
+
127
+ dpl.detailedPlacement(
128
+ self.config["PL_MAX_DISPLACEMENT_X"],
129
+ self.config["PL_MAX_DISPLACEMENT_Y"],
130
+ )
131
+ dpl.reportLegalizationStats()
132
+ dpl.optimizeMirroring()
133
+
134
+ def _grt_setup(self, grt):
135
+ grt.setAdjustment(float(self.config["GRT_ADJUSTMENT"]))
136
+
137
+ routing_layers = [l for l in self.layers.values() if l.getRoutingLevel() >= 1]
138
+ for layer, adj in zip(routing_layers, self.config["GRT_LAYER_ADJUSTMENTS"]):
139
+ grt.addLayerAdjustment(
140
+ layer.getRoutingLevel(),
141
+ float(adj),
142
+ )
143
+
144
+ min_layer_name = self.config["RT_MIN_LAYER"]
145
+ if not min_layer_name in self.layers:
146
+ raise RuntimeError(f"Unknown layer name '{min_layer_name}'")
147
+ min_layer_idx = self.layers[min_layer_name].getRoutingLevel()
148
+
149
+ max_layer_name = self.config["RT_MAX_LAYER"]
150
+ if not max_layer_name in self.layers:
151
+ raise RuntimeError(f"Unknown layer name '{max_layer_name}'")
152
+ max_layer_idx = self.layers[max_layer_name].getRoutingLevel()
153
+
154
+ min_clk_idx = min_layer_idx
155
+ if min_clk_name := self.config["RT_CLOCK_MIN_LAYER"]:
156
+ if not min_clk_name in self.layers:
157
+ raise RuntimeError(f"Unknown layer name '{min_clk_name}'")
158
+ min_clk_idx = self.layers[min_clk_name].getRoutingLevel()
159
+
160
+ max_clk_idx = max_layer_idx
161
+ if max_clk_name := self.config["RT_CLOCK_MAX_LAYER"]:
162
+ if not max_clk_name in self.layers:
163
+ raise RuntimeError(f"Unknown layer name '{max_clk_name}'")
164
+ max_clk_idx = self.layers[max_clk_name].getRoutingLevel()
165
+
166
+ grt.setMinLayerForClock(min_clk_idx)
167
+ grt.setMaxLayerForClock(max_clk_idx)
168
+ grt.setMacroExtension(self.config["GRT_MACRO_EXTENSION"])
169
+ grt.setOverflowIterations(self.config["GRT_OVERFLOW_ITERS"])
170
+ grt.setAllowCongestion(self.config["GRT_ALLOW_CONGESTION"])
171
+ grt.setVerbose(True)
172
+ grt.initFastRoute(min_layer_idx, max_layer_idx)
173
+
174
+ def _grt(self):
175
+ """
176
+ The ``._grt()`` method is EXPERIMENTAL and SHOULD NOT BE USED YET.
177
+
178
+ Use a composite step with ``OpenROAD.GlobalRouting``.
179
+ """
180
+ if self.config is None:
181
+ raise RuntimeError("Attempted to call grt without config file")
182
+
183
+ grt = self.design.getGlobalRouter()
184
+ self._grt_setup(grt)
185
+ grt.globalRoute(
186
+ True
187
+ ) # The first variable updates guides- not sure why the default is False
188
+
189
+
190
+ def click_odb(function):
191
+ @wraps(function)
192
+ def wrapper(input_db, input_lefs, config_path, **kwargs):
193
+ reader = OdbReader(input_db, config_path=config_path)
194
+
195
+ signature = inspect.signature(function)
196
+ parameter_keys = signature.parameters.keys()
197
+
198
+ kwargs = kwargs.copy()
199
+ kwargs["reader"] = reader
200
+
201
+ outputs = []
202
+ for key, value in kwargs.items():
203
+ if key in auto_handled_output_opts:
204
+ id = key[7:]
205
+ outputs.append((id, value))
206
+
207
+ kwargs = {
208
+ k: kwargs[k] for k in kwargs.keys() if not k in auto_handled_output_opts
209
+ }
210
+
211
+ if "input_db" in parameter_keys:
212
+ kwargs["input_db"] = input_db
213
+ if "input_lefs" in parameter_keys:
214
+ kwargs["input_lefs"] = input_lefs
215
+
216
+ if input_db.endswith(".def"):
217
+ print(
218
+ "Error: Invocation was not updated to use an odb file.", file=sys.stderr
219
+ )
220
+ exit(1)
221
+ function(**kwargs)
222
+
223
+ for format, path in outputs:
224
+ fn = write_fn[format]
225
+ fn(reader, path)
226
+
227
+ for format in write_fn:
228
+ wrapper = click.option(
229
+ f"--output-{format}",
230
+ default=None,
231
+ help=f"Write {format} view",
232
+ )(wrapper)
233
+
234
+ wrapper = click.option(
235
+ "-l",
236
+ "--input-lef",
237
+ "input_lefs",
238
+ default=(),
239
+ help="LEF file needed to have a proper view of the DEF files",
240
+ multiple=True,
241
+ )(wrapper)
242
+ wrapper = click.option(
243
+ "--step-config",
244
+ "config_path",
245
+ type=click.Path(exists=True, dir_okay=False, file_okay=True, readable=True),
246
+ required=False,
247
+ )(wrapper)
248
+ wrapper = click.argument("input_db")(wrapper)
249
+
250
+ return wrapper