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.

Files changed (166) hide show
  1. librelane/__init__.py +38 -0
  2. librelane/__main__.py +470 -0
  3. librelane/__version__.py +43 -0
  4. librelane/common/__init__.py +61 -0
  5. librelane/common/cli.py +75 -0
  6. librelane/common/drc.py +245 -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 +402 -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 +117 -0
  19. librelane/config/__init__.py +32 -0
  20. librelane/config/__main__.py +158 -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 +722 -0
  27. librelane/container.py +264 -0
  28. librelane/env_info.py +306 -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 +330 -0
  45. librelane/flows/cli.py +463 -0
  46. librelane/flows/flow.py +985 -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/logging/__init__.py +40 -0
  52. librelane/logging/logger.py +323 -0
  53. librelane/open_pdks_rev +1 -0
  54. librelane/plugins.py +21 -0
  55. librelane/py.typed +0 -0
  56. librelane/scripts/base.sdc +80 -0
  57. librelane/scripts/klayout/Readme.md +2 -0
  58. librelane/scripts/klayout/open_design.py +63 -0
  59. librelane/scripts/klayout/render.py +121 -0
  60. librelane/scripts/klayout/stream_out.py +176 -0
  61. librelane/scripts/klayout/xml_drc_report_to_json.py +45 -0
  62. librelane/scripts/klayout/xor.drc +120 -0
  63. librelane/scripts/magic/Readme.md +1 -0
  64. librelane/scripts/magic/common/read.tcl +114 -0
  65. librelane/scripts/magic/def/antenna_check.tcl +35 -0
  66. librelane/scripts/magic/def/mag.tcl +19 -0
  67. librelane/scripts/magic/def/mag_gds.tcl +81 -0
  68. librelane/scripts/magic/drc.tcl +79 -0
  69. librelane/scripts/magic/extract_spice.tcl +98 -0
  70. librelane/scripts/magic/gds/drc_batch.tcl +74 -0
  71. librelane/scripts/magic/gds/erase_box.tcl +32 -0
  72. librelane/scripts/magic/gds/extras_mag.tcl +47 -0
  73. librelane/scripts/magic/gds/mag_with_pointers.tcl +32 -0
  74. librelane/scripts/magic/get_bbox.tcl +11 -0
  75. librelane/scripts/magic/lef/extras_maglef.tcl +63 -0
  76. librelane/scripts/magic/lef/maglef.tcl +27 -0
  77. librelane/scripts/magic/lef.tcl +57 -0
  78. librelane/scripts/magic/open.tcl +28 -0
  79. librelane/scripts/magic/wrapper.tcl +19 -0
  80. librelane/scripts/netgen/setup.tcl +28 -0
  81. librelane/scripts/odbpy/apply_def_template.py +49 -0
  82. librelane/scripts/odbpy/cell_frequency.py +107 -0
  83. librelane/scripts/odbpy/check_antenna_properties.py +116 -0
  84. librelane/scripts/odbpy/contextualize.py +109 -0
  85. librelane/scripts/odbpy/defutil.py +574 -0
  86. librelane/scripts/odbpy/diodes.py +373 -0
  87. librelane/scripts/odbpy/disconnected_pins.py +305 -0
  88. librelane/scripts/odbpy/exception_codes.py +17 -0
  89. librelane/scripts/odbpy/filter_unannotated.py +100 -0
  90. librelane/scripts/odbpy/io_place.py +482 -0
  91. librelane/scripts/odbpy/label_macro_pins.py +277 -0
  92. librelane/scripts/odbpy/lefutil.py +97 -0
  93. librelane/scripts/odbpy/placers.py +162 -0
  94. librelane/scripts/odbpy/power_utils.py +395 -0
  95. librelane/scripts/odbpy/random_place.py +57 -0
  96. librelane/scripts/odbpy/reader.py +246 -0
  97. librelane/scripts/odbpy/remove_buffers.py +173 -0
  98. librelane/scripts/odbpy/snap_to_grid.py +57 -0
  99. librelane/scripts/odbpy/wire_lengths.py +93 -0
  100. librelane/scripts/openroad/antenna_check.tcl +20 -0
  101. librelane/scripts/openroad/antenna_repair.tcl +31 -0
  102. librelane/scripts/openroad/basic_mp.tcl +24 -0
  103. librelane/scripts/openroad/buffer_list.tcl +10 -0
  104. librelane/scripts/openroad/common/dpl.tcl +24 -0
  105. librelane/scripts/openroad/common/dpl_cell_pad.tcl +26 -0
  106. librelane/scripts/openroad/common/grt.tcl +32 -0
  107. librelane/scripts/openroad/common/io.tcl +476 -0
  108. librelane/scripts/openroad/common/pdn_cfg.tcl +135 -0
  109. librelane/scripts/openroad/common/resizer.tcl +103 -0
  110. librelane/scripts/openroad/common/set_global_connections.tcl +78 -0
  111. librelane/scripts/openroad/common/set_layer_adjustments.tcl +31 -0
  112. librelane/scripts/openroad/common/set_power_nets.tcl +30 -0
  113. librelane/scripts/openroad/common/set_rc.tcl +75 -0
  114. librelane/scripts/openroad/common/set_routing_layers.tcl +30 -0
  115. librelane/scripts/openroad/cts.tcl +80 -0
  116. librelane/scripts/openroad/cut_rows.tcl +24 -0
  117. librelane/scripts/openroad/dpl.tcl +24 -0
  118. librelane/scripts/openroad/drt.tcl +37 -0
  119. librelane/scripts/openroad/fill.tcl +30 -0
  120. librelane/scripts/openroad/floorplan.tcl +145 -0
  121. librelane/scripts/openroad/gpl.tcl +88 -0
  122. librelane/scripts/openroad/grt.tcl +30 -0
  123. librelane/scripts/openroad/gui.tcl +15 -0
  124. librelane/scripts/openroad/insert_buffer.tcl +127 -0
  125. librelane/scripts/openroad/ioplacer.tcl +67 -0
  126. librelane/scripts/openroad/irdrop.tcl +51 -0
  127. librelane/scripts/openroad/pdn.tcl +52 -0
  128. librelane/scripts/openroad/rcx.tcl +32 -0
  129. librelane/scripts/openroad/repair_design.tcl +70 -0
  130. librelane/scripts/openroad/repair_design_postgrt.tcl +48 -0
  131. librelane/scripts/openroad/rsz_timing_postcts.tcl +68 -0
  132. librelane/scripts/openroad/rsz_timing_postgrt.tcl +70 -0
  133. librelane/scripts/openroad/sta/check_macro_instances.tcl +53 -0
  134. librelane/scripts/openroad/sta/corner.tcl +393 -0
  135. librelane/scripts/openroad/tapcell.tcl +25 -0
  136. librelane/scripts/openroad/write_views.tcl +27 -0
  137. librelane/scripts/pyosys/construct_abc_script.py +177 -0
  138. librelane/scripts/pyosys/json_header.py +84 -0
  139. librelane/scripts/pyosys/synthesize.py +493 -0
  140. librelane/scripts/pyosys/ys_common.py +153 -0
  141. librelane/scripts/tclsh/hello.tcl +1 -0
  142. librelane/state/__init__.py +24 -0
  143. librelane/state/__main__.py +61 -0
  144. librelane/state/design_format.py +180 -0
  145. librelane/state/state.py +351 -0
  146. librelane/steps/__init__.py +61 -0
  147. librelane/steps/__main__.py +511 -0
  148. librelane/steps/checker.py +637 -0
  149. librelane/steps/common_variables.py +340 -0
  150. librelane/steps/cvc_rv.py +169 -0
  151. librelane/steps/klayout.py +509 -0
  152. librelane/steps/magic.py +566 -0
  153. librelane/steps/misc.py +160 -0
  154. librelane/steps/netgen.py +253 -0
  155. librelane/steps/odb.py +955 -0
  156. librelane/steps/openroad.py +2433 -0
  157. librelane/steps/openroad_alerts.py +102 -0
  158. librelane/steps/pyosys.py +629 -0
  159. librelane/steps/step.py +1547 -0
  160. librelane/steps/tclstep.py +288 -0
  161. librelane/steps/verilator.py +222 -0
  162. librelane/steps/yosys.py +371 -0
  163. librelane-2.4.0.dev0.dist-info/METADATA +151 -0
  164. librelane-2.4.0.dev0.dist-info/RECORD +166 -0
  165. librelane-2.4.0.dev0.dist-info/WHEEL +4 -0
  166. librelane-2.4.0.dev0.dist-info/entry_points.txt +8 -0
@@ -0,0 +1,173 @@
1
+ # Copyright 2023 Efabless Corporation
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from __future__ import annotations
15
+ from decimal import Decimal
16
+ import os
17
+
18
+ import rich
19
+ import rich.table
20
+ from concurrent.futures import Future
21
+ from typing import Dict, List, Optional, Tuple
22
+
23
+ from .flow import Flow
24
+ from ..state import State
25
+ from ..config import Config
26
+ from ..logging import success
27
+ from ..logging import options, console
28
+ from ..steps import Step, Yosys, OpenROAD, StepError
29
+
30
+
31
+ # "Synthesis Exploration" is a non-seqeuential flow that tries all synthesis
32
+ # strategies and shows which ones yield the best area XOR delay
33
+ @Flow.factory.register()
34
+ class SynthesisExploration(Flow):
35
+ """
36
+ Synthesis Exploration is a feature that tries multiple synthesis strategies
37
+ (in the form of different scripts for the ABC utility) to try and find which
38
+ strategy is better by either minimizing area or maximizing slack (and thus
39
+ frequency.)
40
+
41
+ The output is represented in a tabulated format, e.g.: ::
42
+
43
+ ┏━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┓
44
+ ┃ ┃ ┃ ┃ Worst Setup ┃ Total Negative ┃
45
+ ┃ SYNTH_STRATEGY ┃ Gates ┃ Area (µm²) ┃ Slack (ns) ┃ Setup Slack (ns) ┃
46
+ ┡━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━┩
47
+ │ AREA 0 │ 8781 │ 97141.916800 │ 6.288794 │ 0.0 │
48
+ │ AREA 1 │ 8692 │ 96447.500800 │ 6.434102 │ 0.0 │
49
+ │ AREA 2 │ 8681 │ 96339.897600 │ 6.276806 │ 0.0 │
50
+ │ AREA 3 │ 11793 │ 111084.038400 │ 7.011374 │ 0.0 │
51
+ │ DELAY 0 │ 8969 │ 101418.518400 │ 6.511191 │ 0.0 │
52
+ │ DELAY 1 │ 8997 │ 101275.881600 │ 6.656564 │ 0.0 │
53
+ │ DELAY 2 │ 9013 │ 101177.036800 │ 6.691765 │ 0.0 │
54
+ │ DELAY 3 │ 8733 │ 99190.131200 │ 6.414865 │ 0.0 │
55
+ │ DELAY 4 │ 8739 │ 101011.878400 │ 6.274565 │ 0.0 │
56
+ └────────────────┴───────┴───────────────┴──────────────────┴──────────────────┘
57
+
58
+ You can then update your config file with the best ``SYNTH_STRATEGY`` for your
59
+ use-case so it can be used with other flows.
60
+ """
61
+
62
+ Steps = [
63
+ Yosys.Synthesis,
64
+ OpenROAD.CheckSDCFiles,
65
+ OpenROAD.STAPrePNR,
66
+ ]
67
+
68
+ def run(
69
+ self,
70
+ initial_state: State,
71
+ **kwargs,
72
+ ) -> Tuple[State, List[Step]]:
73
+ step_list: List[Step] = []
74
+
75
+ self.progress_bar.set_max_stage_count(1)
76
+
77
+ synth_futures: List[Tuple[Config, Future[State]]] = []
78
+ self.progress_bar.start_stage("Synthesis Exploration")
79
+
80
+ options.set_condensed_mode(True)
81
+
82
+ for strategy in [
83
+ "AREA 0",
84
+ "AREA 1",
85
+ "AREA 2",
86
+ "AREA 3",
87
+ "DELAY 0",
88
+ "DELAY 1",
89
+ "DELAY 2",
90
+ "DELAY 3",
91
+ "DELAY 4",
92
+ ]:
93
+ config = self.config.copy(SYNTH_STRATEGY=strategy)
94
+
95
+ synth_step = Yosys.Synthesis(
96
+ config,
97
+ id=f"synthesis-{strategy}",
98
+ state_in=initial_state,
99
+ )
100
+ synth_future = self.start_step_async(synth_step)
101
+ step_list.append(synth_step)
102
+
103
+ sdc_step = OpenROAD.CheckSDCFiles(
104
+ config,
105
+ id=f"sdc-{strategy}",
106
+ state_in=synth_future,
107
+ )
108
+ sdc_future = self.start_step_async(sdc_step)
109
+ step_list.append(sdc_step)
110
+
111
+ sta_step = OpenROAD.STAPrePNR(
112
+ config,
113
+ state_in=sdc_future,
114
+ id=f"sta-{strategy}",
115
+ )
116
+
117
+ step_list.append(sta_step)
118
+ sta_future = self.start_step_async(sta_step)
119
+
120
+ synth_futures.append((config, sta_future))
121
+
122
+ results: Dict[str, Optional[Tuple[Decimal, Decimal, Decimal, Decimal]]] = {}
123
+ for config, future in synth_futures:
124
+ strategy = config["SYNTH_STRATEGY"]
125
+ results[strategy] = None
126
+ try:
127
+ state = future.result()
128
+ results[strategy] = (
129
+ state.metrics["design__instance__count"],
130
+ state.metrics["design__instance__area"],
131
+ state.metrics["timing__setup__ws"],
132
+ state.metrics["timing__setup__tns"],
133
+ )
134
+ except StepError:
135
+ pass # None == failure
136
+ self.progress_bar.end_stage()
137
+ options.set_condensed_mode(False)
138
+
139
+ successful_results = {k: v for k, v in results.items() if v is not None}
140
+ min_gates = min(map(lambda x: x[0], successful_results.values()))
141
+ min_area = min(map(lambda x: x[1], successful_results.values()))
142
+ max_slack = max(map(lambda x: x[2], successful_results.values()))
143
+ max_tns = max(map(lambda x: x[3], successful_results.values()))
144
+
145
+ table = rich.table.Table()
146
+ table.add_column("SYNTH_STRATEGY")
147
+ table.add_column("Gates")
148
+ table.add_column("Area (µm²)")
149
+ table.add_column("Worst Setup Slack (ns)")
150
+ table.add_column("Total -ve Setup Slack (ns)")
151
+ for key, result in results.items():
152
+ gates_s = "[red]Failed"
153
+ area_s = "[red]Failed"
154
+ slack_s = "[red]Failed"
155
+ tns_s = "[red]Failed"
156
+ if result is not None:
157
+ gates, area, slack, tns = result
158
+ gates_s = f"{'[green]' if gates == min_gates else ''}{gates}"
159
+ area_s = f"{'[green]' if area == min_area else ''}{area}"
160
+ slack_s = f"{'[green]' if slack == max_slack else ''}{slack}"
161
+ tns_s = f"{'[green]' if tns == max_tns else ''}{tns}"
162
+ table.add_row(key, gates_s, area_s, slack_s, tns_s)
163
+
164
+ console.print(table)
165
+ assert self.run_dir is not None
166
+ file_console = rich.console.Console(
167
+ file=open(os.path.join(self.run_dir, "summary.rpt"), "w", encoding="utf8"),
168
+ width=160,
169
+ )
170
+ file_console.print(table)
171
+
172
+ success("Flow complete.")
173
+ return (initial_state, step_list)
@@ -0,0 +1,40 @@
1
+ # Copyright 2023 Efabless Corporation
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """
15
+ The Logging Module
16
+ ------------------
17
+
18
+ As the name implies, this handles LibreLane's logging using the ``logging``
19
+ module and the ``rich`` library.
20
+ """
21
+
22
+ from .logger import (
23
+ LogLevels,
24
+ LevelFilter,
25
+ options,
26
+ console,
27
+ set_log_level,
28
+ reset_log_level,
29
+ get_log_level,
30
+ register_additional_handler,
31
+ deregister_additional_handler,
32
+ verbose,
33
+ debug,
34
+ info,
35
+ rule,
36
+ success,
37
+ warn,
38
+ err,
39
+ subprocess,
40
+ )
@@ -0,0 +1,323 @@
1
+ # Copyright 2023 Efabless Corporation
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import click
15
+ import atexit
16
+ import logging
17
+ from enum import IntEnum
18
+ from typing import ClassVar, Iterable, Union
19
+
20
+ import rich.console
21
+ import rich.logging
22
+ from rich.text import Text
23
+ from rich.style import Style, StyleType
24
+
25
+
26
+ class LogLevels(IntEnum):
27
+ ALL = 0
28
+ DEBUG = 10
29
+ SUBPROCESS = 12
30
+ VERBOSE = 15
31
+ INFO = 20
32
+ WARNING = 30
33
+ ERROR = 40
34
+ CRITICAL = 50
35
+
36
+
37
+ console = rich.console.Console()
38
+ atexit.register(lambda: rich.console.Console().show_cursor())
39
+ __event_logger: logging.Logger = logging.getLogger("__librelane__")
40
+
41
+
42
+ class options:
43
+ _condensed_mode: ClassVar[bool] = False
44
+ _show_progress_bar: ClassVar[bool] = True
45
+
46
+ @classmethod
47
+ def get_condensed_mode(Self) -> bool:
48
+ return Self._condensed_mode
49
+
50
+ @classmethod
51
+ def set_condensed_mode(Self, condensed: bool):
52
+ Self._condensed_mode = condensed
53
+
54
+ @classmethod
55
+ def get_show_progress_bar(Self) -> bool:
56
+ return Self._show_progress_bar
57
+
58
+ @classmethod
59
+ def set_show_progress_bar(Self, show: bool):
60
+ Self._show_progress_bar = show
61
+
62
+
63
+ class NullFormatter(logging.Formatter):
64
+ def format(self, record):
65
+ return record.getMessage()
66
+
67
+
68
+ class LevelFormatter(logging.Formatter):
69
+ def format(self, record):
70
+ message = record.getMessage()
71
+ if record.levelname == "WARNING":
72
+ message = f"[yellow]{message}"
73
+ elif record.levelname == "ERROR":
74
+ message = f"[red]{message}"
75
+ elif record.levelname == "CRITICAL":
76
+ message = f"[red][bold]{message}"
77
+ else:
78
+ message = f"{message}"
79
+ return message
80
+
81
+
82
+ class RichHandler(rich.logging.RichHandler):
83
+ def __init__(self, *args, **kwargs) -> None:
84
+ super().__init__(**kwargs)
85
+
86
+ def get_level_text(self, record: logging.LogRecord) -> Text:
87
+ if not options.get_condensed_mode():
88
+ return super().get_level_text(record)
89
+ level_name = record.levelname
90
+ style: StyleType
91
+ if level_name == "WARNING":
92
+ style = Style(color="yellow", bold=True)
93
+ else:
94
+ style = f"logging.level.{level_name.lower()}"
95
+ level_text = Text.styled(
96
+ f"[{level_name[0]}]",
97
+ style,
98
+ )
99
+ return level_text
100
+
101
+
102
+ class KeywordFilter(logging.Filter):
103
+ def __init__(self, matching_values: dict) -> None:
104
+ super().__init__()
105
+ self.matching_values = matching_values.copy()
106
+
107
+ def filter(self, record: logging.LogRecord) -> bool:
108
+ for key, value in self.matching_values.items():
109
+ if value is None:
110
+ if hasattr(record, key) and getattr(record, key) is not None:
111
+ return False
112
+ else:
113
+ if not hasattr(record, key) or getattr(record, key) != value:
114
+ return False
115
+ return True
116
+
117
+
118
+ class LevelFilter(logging.Filter):
119
+ def __init__(self, levels: Iterable[str], invert: bool = False) -> None:
120
+ self.levels = levels
121
+ self.invert = invert
122
+
123
+ def filter(self, record: logging.LogRecord) -> bool:
124
+ if options.get_condensed_mode():
125
+ if record.levelname == "SUBPROCESS":
126
+ return False
127
+ if self.invert:
128
+ return record.levelname not in self.levels
129
+ else:
130
+ return record.levelname in self.levels
131
+
132
+
133
+ def initialize_logger():
134
+ global __event_logger, console
135
+
136
+ for level in LogLevels:
137
+ logging.addLevelName(level.value, level.name)
138
+
139
+ subprocess_handler = RichHandler(
140
+ console=console,
141
+ show_time=False,
142
+ omit_repeated_times=False,
143
+ show_level=False,
144
+ show_path=False,
145
+ enable_link_path=False,
146
+ tracebacks_word_wrap=False,
147
+ keywords=[],
148
+ markup=False,
149
+ )
150
+ subprocess_handler.addFilter(LevelFilter(["SUBPROCESS"]))
151
+
152
+ rich_handler = RichHandler(
153
+ console=console,
154
+ rich_tracebacks=True,
155
+ omit_repeated_times=False,
156
+ markup=True,
157
+ tracebacks_suppress=[
158
+ click,
159
+ ],
160
+ show_level=True,
161
+ keywords=[],
162
+ )
163
+ rich_handler.setFormatter(LevelFormatter("%(message)s", datefmt="[%X]"))
164
+ rich_handler.addFilter(LevelFilter(["SUBPROCESS"], invert=True))
165
+
166
+ logger = logging.getLogger("__librelane__")
167
+ logger.setLevel(LogLevels.SUBPROCESS)
168
+
169
+ logger.handlers.clear()
170
+
171
+ logger.addHandler(subprocess_handler)
172
+ logger.addHandler(rich_handler)
173
+
174
+
175
+ initialize_logger()
176
+
177
+
178
+ def register_additional_handler(handler: logging.Handler):
179
+ """
180
+ Adds a new handler to the default LibreLane logger.
181
+
182
+ :param handler: The new handler. Must be of type ``logging.Handler``
183
+ or its subclasses.
184
+ """
185
+ __event_logger.addHandler(handler)
186
+
187
+
188
+ def deregister_additional_handler(handler: logging.Handler):
189
+ """
190
+ Removes a registered handler from the default LibreLane logger.
191
+
192
+ :param handler: The handler. If not registered, the behavior
193
+ of this function is undefined.
194
+ """
195
+ __event_logger.removeHandler(handler)
196
+
197
+
198
+ def set_log_level(lv: Union[str, int]):
199
+ """
200
+ Sets the log level of the default LibreLane logger.
201
+
202
+ :param lv: Either the name or number of the desired log level.
203
+ """
204
+ __event_logger.setLevel(lv)
205
+
206
+
207
+ def reset_log_level():
208
+ """
209
+ Sets the log level of the default LibreLane logger back to the
210
+ default log level.
211
+ """
212
+ set_log_level("SUBPROCESS")
213
+
214
+
215
+ def get_log_level() -> int:
216
+ """
217
+ Obtains the numeric log level of the LibreLane logger.
218
+ """
219
+ return __event_logger.getEffectiveLevel()
220
+
221
+
222
+ def debug(*args, **kwargs):
223
+ """
224
+ Logs to the LibreLane logger with the log level DEBUG.
225
+
226
+ :param msg: The message to log
227
+ """
228
+ if kwargs.get("stacklevel") is None:
229
+ kwargs["stacklevel"] = 2
230
+ __event_logger.debug(*args, **kwargs)
231
+
232
+
233
+ def verbose(*args, **kwargs):
234
+ """
235
+ Logs to the LibreLane logger with the log level VERBOSE.
236
+ """
237
+ if kwargs.get("stacklevel") is None:
238
+ kwargs["stacklevel"] = 2
239
+ __event_logger.log(
240
+ LogLevels.VERBOSE,
241
+ *args,
242
+ **kwargs,
243
+ )
244
+
245
+
246
+ def info(msg: object, /, **kwargs):
247
+ """
248
+ Logs to the LibreLane logger with the log level INFO.
249
+
250
+ :param msg: The message to log
251
+ """
252
+ if kwargs.get("stacklevel") is None:
253
+ kwargs["stacklevel"] = 2
254
+ __event_logger.info(msg, **kwargs)
255
+
256
+
257
+ def subprocess(msg: object, /, **kwargs):
258
+ """
259
+ Logs to the LibreLane logger with the log level SUBPROCESS.
260
+
261
+ :param msg: The message to log
262
+ """
263
+ if kwargs.get("stacklevel") is None:
264
+ kwargs["stacklevel"] = 2
265
+ __event_logger.log(LogLevels.SUBPROCESS, msg, **kwargs)
266
+
267
+
268
+ def rule(title: str = "", /, **kwargs): # pragma: no cover
269
+ """
270
+ Prints a horizontal line on the terminal enclosing the first argument
271
+ if the log level is <= INFO.
272
+
273
+ Kwargs are passed to https://rich.readthedocs.io/en/stable/reference/console.html#rich.console.Console.rule
274
+
275
+ :param title: A title string to enclose in the console rule
276
+ """
277
+ console.rule(title)
278
+
279
+
280
+ def success(msg: object, /, **kwargs):
281
+ """
282
+ Logs to the LibreLane logger with the log level INFO.
283
+
284
+ :param msg: The message to log
285
+ """
286
+ if kwargs.get("stacklevel") is None:
287
+ kwargs["stacklevel"] = 2
288
+ __event_logger.info(f"{msg}", **kwargs)
289
+
290
+
291
+ def warn(msg: object, /, **kwargs):
292
+ """
293
+ Logs to the LibreLane logger with the log level WARNING.
294
+
295
+ :param msg: The message to log
296
+ """
297
+ if kwargs.get("stacklevel") is None:
298
+ kwargs["stacklevel"] = 2
299
+ __event_logger.warning(f"{msg}", **kwargs)
300
+
301
+
302
+ def err(msg: object, /, **kwargs):
303
+ """
304
+ Logs to the LibreLane logger with the log level ERROR.
305
+
306
+ :param msg: The message to log
307
+ """
308
+ if kwargs.get("stacklevel") is None:
309
+ kwargs["stacklevel"] = 2
310
+ __event_logger.error(f"{msg}", **kwargs)
311
+
312
+
313
+ if __name__ == "__main__":
314
+ initialize_logger()
315
+ debug("Debug")
316
+ verbose("Verbose")
317
+ subprocess("Subprocess")
318
+ rule("Rule")
319
+ info("Info")
320
+ success("Success")
321
+ warn("Warn")
322
+ err("Err")
323
+ print("\n")
@@ -0,0 +1 @@
1
+ 0fe599b2afb6708d281543108caf8310912f54af
librelane/plugins.py ADDED
@@ -0,0 +1,21 @@
1
+ # Copyright 2023 Efabless Corporation
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import pkgutil
15
+ import importlib
16
+
17
+ discovered_plugins = {
18
+ name: importlib.import_module(name)
19
+ for finder, name, ispkg in pkgutil.iter_modules()
20
+ if name.startswith("librelane_plugin_") or name.startswith("openlane_plugin_")
21
+ }
librelane/py.typed ADDED
File without changes
@@ -0,0 +1,80 @@
1
+ set clock_port __VIRTUAL_CLK__
2
+ if { [info exists ::env(CLOCK_PORT)] } {
3
+ set port_count [llength $::env(CLOCK_PORT)]
4
+
5
+ if { $port_count == "0" } {
6
+ puts "\[WARNING] No CLOCK_PORT found. A dummy clock will be used."
7
+ } elseif { $port_count != "1" } {
8
+ puts "\[WARNING] Multi-clock files are not currently supported by the base SDC file. Only the first clock will be constrained."
9
+ }
10
+
11
+ if { $port_count > "0" } {
12
+ set ::clock_port [lindex $::env(CLOCK_PORT) 0]
13
+ }
14
+ }
15
+ set port_args [get_ports $clock_port]
16
+ puts "\[INFO] Using clock $clock_port…"
17
+ create_clock {*}$port_args -name $clock_port -period $::env(CLOCK_PERIOD)
18
+
19
+ set input_delay_value [expr $::env(CLOCK_PERIOD) * $::env(IO_DELAY_CONSTRAINT) / 100]
20
+ set output_delay_value [expr $::env(CLOCK_PERIOD) * $::env(IO_DELAY_CONSTRAINT) / 100]
21
+ puts "\[INFO] Setting output delay to: $output_delay_value"
22
+ puts "\[INFO] Setting input delay to: $input_delay_value"
23
+
24
+ set_max_fanout $::env(MAX_FANOUT_CONSTRAINT) [current_design]
25
+ if { [info exists ::env(MAX_TRANSITION_CONSTRAINT)] } {
26
+ set_max_transition $::env(MAX_TRANSITION_CONSTRAINT) [current_design]
27
+ }
28
+ if { [info exists ::env(MAX_CAPACITANCE_CONSTRAINT)] } {
29
+ set_max_capacitance $::env(MAX_CAPACITANCE_CONSTRAINT) [current_design]
30
+ }
31
+
32
+ set clk_input [get_port $clock_port]
33
+ set clk_indx [lsearch [all_inputs] $clk_input]
34
+ set all_inputs_wo_clk [lreplace [all_inputs] $clk_indx $clk_indx ""]
35
+
36
+ #set rst_input [get_port resetn]
37
+ #set rst_indx [lsearch [all_inputs] $rst_input]
38
+ #set all_inputs_wo_clk_rst [lreplace $all_inputs_wo_clk $rst_indx $rst_indx ""]
39
+ set all_inputs_wo_clk_rst $all_inputs_wo_clk
40
+
41
+ # correct resetn
42
+ set clocks [get_clocks $clock_port]
43
+
44
+ set_input_delay $input_delay_value -clock $clocks $all_inputs_wo_clk_rst
45
+ set_output_delay $output_delay_value -clock $clocks [all_outputs]
46
+
47
+ if { ![info exists ::env(SYNTH_CLK_DRIVING_CELL)] } {
48
+ set ::env(SYNTH_CLK_DRIVING_CELL) $::env(SYNTH_DRIVING_CELL)
49
+ }
50
+
51
+ set_driving_cell \
52
+ -lib_cell [lindex [split $::env(SYNTH_DRIVING_CELL) "/"] 0] \
53
+ -pin [lindex [split $::env(SYNTH_DRIVING_CELL) "/"] 1] \
54
+ $all_inputs_wo_clk_rst
55
+
56
+ set_driving_cell \
57
+ -lib_cell [lindex [split $::env(SYNTH_CLK_DRIVING_CELL) "/"] 0] \
58
+ -pin [lindex [split $::env(SYNTH_CLK_DRIVING_CELL) "/"] 1] \
59
+ $clk_input
60
+
61
+ set cap_load [expr $::env(OUTPUT_CAP_LOAD) / 1000.0]
62
+ puts "\[INFO] Setting load to: $cap_load"
63
+ set_load $cap_load [all_outputs]
64
+
65
+ puts "\[INFO] Setting clock uncertainty to: $::env(CLOCK_UNCERTAINTY_CONSTRAINT)"
66
+ set_clock_uncertainty $::env(CLOCK_UNCERTAINTY_CONSTRAINT) $clocks
67
+
68
+ puts "\[INFO] Setting clock transition to: $::env(CLOCK_TRANSITION_CONSTRAINT)"
69
+ set_clock_transition $::env(CLOCK_TRANSITION_CONSTRAINT) $clocks
70
+
71
+ puts "\[INFO] Setting timing derate to: $::env(TIME_DERATING_CONSTRAINT)%"
72
+ set_timing_derate -early [expr 1-[expr $::env(TIME_DERATING_CONSTRAINT) / 100]]
73
+ set_timing_derate -late [expr 1+[expr $::env(TIME_DERATING_CONSTRAINT) / 100]]
74
+
75
+ if { [info exists ::env(OPENLANE_SDC_IDEAL_CLOCKS)] && $::env(OPENLANE_SDC_IDEAL_CLOCKS) } {
76
+ unset_propagated_clock [all_clocks]
77
+ } else {
78
+ set_propagated_clock [all_clocks]
79
+ }
80
+
@@ -0,0 +1,2 @@
1
+ ## Reference
2
+ https://www.klayout.org/downloads/pymod/doc-qt5/code/index.html