librelane 2.4.0.dev8__py3-none-any.whl → 3.0.0.dev22__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 (62) hide show
  1. librelane/__main__.py +12 -15
  2. librelane/common/__init__.py +1 -1
  3. librelane/common/drc.py +88 -7
  4. librelane/common/misc.py +6 -6
  5. librelane/common/toolbox.py +1 -1
  6. librelane/config/config.py +5 -1
  7. librelane/config/flow.py +51 -66
  8. librelane/config/pdk_compat.py +79 -2
  9. librelane/config/preprocessor.py +1 -1
  10. librelane/config/variable.py +2 -2
  11. librelane/flows/classic.py +1 -0
  12. librelane/flows/flow.py +3 -6
  13. librelane/flows/sequential.py +85 -40
  14. librelane/plugins.py +1 -1
  15. librelane/scripts/magic/common/read.tcl +2 -2
  16. librelane/scripts/magic/gds/extras_mag.tcl +2 -2
  17. librelane/scripts/odbpy/diodes.py +2 -2
  18. librelane/scripts/openroad/common/dpl.tcl +1 -1
  19. librelane/scripts/openroad/common/grt.tcl +3 -3
  20. librelane/scripts/openroad/common/io.tcl +163 -45
  21. librelane/scripts/openroad/common/resizer.tcl +1 -40
  22. librelane/scripts/openroad/common/set_global_connections.tcl +2 -2
  23. librelane/scripts/openroad/common/set_power_nets.tcl +1 -1
  24. librelane/scripts/openroad/common/set_rc.tcl +159 -40
  25. librelane/scripts/openroad/cts.tcl +37 -6
  26. librelane/scripts/openroad/cut_rows.tcl +19 -4
  27. librelane/scripts/openroad/drt.tcl +59 -8
  28. librelane/scripts/openroad/dump_rc.tcl +105 -0
  29. librelane/scripts/openroad/fill.tcl +2 -2
  30. librelane/scripts/openroad/floorplan.tcl +5 -3
  31. librelane/scripts/openroad/gpl.tcl +7 -8
  32. librelane/scripts/openroad/insert_buffer.tcl +2 -2
  33. librelane/scripts/openroad/ioplacer.tcl +1 -2
  34. librelane/scripts/openroad/irdrop.tcl +3 -3
  35. librelane/scripts/openroad/pdn.tcl +17 -18
  36. librelane/scripts/openroad/rcx.tcl +1 -1
  37. librelane/scripts/openroad/repair_design.tcl +14 -7
  38. librelane/scripts/openroad/repair_design_postgrt.tcl +13 -6
  39. librelane/scripts/openroad/rsz_timing_postcts.tcl +13 -12
  40. librelane/scripts/openroad/rsz_timing_postgrt.tcl +13 -12
  41. librelane/scripts/openroad/sta/check_macro_instances.tcl +1 -1
  42. librelane/scripts/openroad/tapcell.tcl +13 -6
  43. librelane/scripts/openroad/ungpl.tcl +23 -0
  44. librelane/state/__init__.py +1 -1
  45. librelane/state/design_format.py +194 -142
  46. librelane/state/state.py +20 -21
  47. librelane/steps/checker.py +12 -1
  48. librelane/steps/common_variables.py +4 -4
  49. librelane/steps/cvc_rv.py +1 -1
  50. librelane/steps/klayout.py +14 -6
  51. librelane/steps/magic.py +18 -2
  52. librelane/steps/misc.py +1 -1
  53. librelane/steps/odb.py +50 -31
  54. librelane/steps/openroad.py +455 -128
  55. librelane/steps/pyosys.py +20 -5
  56. librelane/steps/step.py +17 -20
  57. librelane/steps/tclstep.py +9 -7
  58. librelane/steps/yosys.py +1 -1
  59. {librelane-2.4.0.dev8.dist-info → librelane-3.0.0.dev22.dist-info}/METADATA +1 -1
  60. {librelane-2.4.0.dev8.dist-info → librelane-3.0.0.dev22.dist-info}/RECORD +62 -60
  61. {librelane-2.4.0.dev8.dist-info → librelane-3.0.0.dev22.dist-info}/WHEEL +0 -0
  62. {librelane-2.4.0.dev8.dist-info → librelane-3.0.0.dev22.dist-info}/entry_points.txt +0 -0
librelane/state/state.py CHANGED
@@ -22,7 +22,6 @@ from typing import Callable, List, Mapping, Tuple, Union, Optional, Dict, Any
22
22
 
23
23
  from .design_format import (
24
24
  DesignFormat,
25
- DesignFormatObject,
26
25
  )
27
26
 
28
27
  from ..common import (
@@ -91,26 +90,21 @@ class State(GenericImmutableDict[str, StateElement]):
91
90
  if c_mapping := copying:
92
91
  for key, value in c_mapping.items():
93
92
  if isinstance(key, DesignFormat):
94
- copying_resolved[key.value.id] = value
93
+ copying_resolved[key.id] = value
95
94
  else:
96
95
  copying_resolved[key] = value
97
96
 
98
- for format in DesignFormat:
99
- assert isinstance(format.value, DesignFormatObject) # type checker shut up
100
- if format.value.id not in copying_resolved:
101
- copying_resolved[format.value.id] = None
102
-
103
97
  overrides_resolved = {}
104
98
  if o_mapping := overrides:
105
99
  for k, value in o_mapping.items():
106
100
  if isinstance(k, DesignFormat):
107
- assert isinstance(
108
- k.value, DesignFormatObject
109
- ) # type checker shut up
110
- k = k.value.id
101
+ k = k.id
111
102
  overrides_resolved[k] = value
112
103
 
113
104
  self.metrics = GenericImmutableDict(metrics or {})
105
+ for key in list(copying_resolved.keys()):
106
+ if copying_resolved[key] is None:
107
+ del copying_resolved[key]
114
108
 
115
109
  super().__init__(
116
110
  copying_resolved,
@@ -119,21 +113,24 @@ class State(GenericImmutableDict[str, StateElement]):
119
113
  **kwargs,
120
114
  )
121
115
 
116
+ def get_by_df(self, key: DesignFormat) -> StateElement:
117
+ return self.get(key.id)
118
+
122
119
  def __getitem__(self, key: Union[DesignFormat, str]) -> StateElement:
123
120
  if isinstance(key, DesignFormat):
124
- id: str = key.value.id
121
+ id: str = key.id
125
122
  key = id
126
123
  return super().__getitem__(key)
127
124
 
128
125
  def __setitem__(self, key: Union[DesignFormat, str], item: StateElement):
129
126
  if isinstance(key, DesignFormat):
130
- id: str = key.value.id
127
+ id: str = key.id
131
128
  key = id
132
129
  return super().__setitem__(key, item)
133
130
 
134
131
  def __delitem__(self, key: Union[DesignFormat, str]):
135
132
  if isinstance(key, DesignFormat):
136
- id: str = key.value.id
133
+ id: str = key.id
137
134
  key = id
138
135
  return super().__delitem__(key)
139
136
 
@@ -164,10 +161,8 @@ class State(GenericImmutableDict[str, StateElement]):
164
161
  if current_top_key is None:
165
162
  current_top_key = key
166
163
  current_folder = key.strip("_*")
167
- if df := DesignFormat.by_id(key):
168
- # For type-checker: all guaranteed to be DesignFormatObjects
169
- assert isinstance(df.value, DesignFormatObject)
170
- current_folder = df.value.folder
164
+ if df := DesignFormat.factory.get(key):
165
+ current_folder = df.folder
171
166
 
172
167
  target_dir = os.path.join(save_directory, current_folder)
173
168
 
@@ -228,7 +223,11 @@ class State(GenericImmutableDict[str, StateElement]):
228
223
  """
229
224
 
230
225
  def visitor(key, value, top_key, _, depth):
231
- if depth == 0 and DesignFormat.by_id(top_key) is None:
226
+ if (
227
+ depth == 0
228
+ and DesignFormat.factory.get(top_key) is None
229
+ and value is not None
230
+ ):
232
231
  raise InvalidState(
233
232
  f"Key '{top_key}' does not match a known design format."
234
233
  )
@@ -314,8 +313,8 @@ class State(GenericImmutableDict[str, StateElement]):
314
313
  continue
315
314
 
316
315
  key_content = id
317
- if format := DesignFormat.by_id(id):
318
- key_content = format.value.id
316
+ if format := DesignFormat.factory.get(id):
317
+ key_content = format.id
319
318
 
320
319
  value_content = str(value)
321
320
  if isinstance(value, Mapping):
@@ -266,7 +266,16 @@ class WireLength(MetricChecker):
266
266
  default=True,
267
267
  deprecated_names=["QUIT_ON_LONG_WIRE"],
268
268
  )
269
- config_vars = [error_on_var]
269
+ config_vars = [
270
+ error_on_var,
271
+ Variable(
272
+ "WIRE_LENGTH_THRESHOLD",
273
+ Optional[Decimal],
274
+ "A value above which wire lengths generate warnings.",
275
+ units="µm",
276
+ pdk=True,
277
+ ),
278
+ ]
270
279
 
271
280
  def get_threshold(self) -> Optional[Decimal]:
272
281
  threshold = self.config["WIRE_LENGTH_THRESHOLD"]
@@ -477,6 +486,7 @@ class TimingViolations(MetricChecker):
477
486
  cls.base_corner_var_name.replace("TIMING", replace_by),
478
487
  Optional[List[str]],
479
488
  f"A list of wildcards matching IPVT corners to use during checking for {cls.violation_type} violations.",
489
+ pdk=True,
480
490
  )
481
491
  if cls.corner_override:
482
492
  variable.default = cls.corner_override
@@ -635,3 +645,4 @@ class HoldViolations(TimingViolations):
635
645
  violation_type = "hold"
636
646
 
637
647
  metric_name = "timing__hold_vio__count"
648
+ corner_override = ["*"]
@@ -261,21 +261,21 @@ dpl_variables = [
261
261
  ),
262
262
  Variable(
263
263
  "PL_MAX_DISPLACEMENT_X",
264
- Decimal,
264
+ int,
265
265
  "Specifies how far an instance can be moved along the X-axis when finding a site where it can be placed during detailed placement.",
266
266
  default=500,
267
267
  units="µm",
268
268
  ),
269
269
  Variable(
270
270
  "PL_MAX_DISPLACEMENT_Y",
271
- Decimal,
271
+ int,
272
272
  "Specifies how far an instance can be moved along the Y-axis when finding a site where it can be placed during detailed placement.",
273
273
  default=100,
274
274
  units="µm",
275
275
  ),
276
276
  Variable(
277
277
  "DPL_CELL_PADDING",
278
- Decimal,
278
+ int,
279
279
  "Cell padding value (in sites) for detailed placement. The number will be integer divided by 2 and placed on both sides. Should be <= global placement.",
280
280
  units="sites",
281
281
  pdk=True,
@@ -335,6 +335,6 @@ rsz_variables = dpl_variables + [
335
335
  Variable(
336
336
  "RSZ_CORNERS",
337
337
  Optional[List[str]],
338
- "A list of fully-qualified IPVT corners to use during resizer optimizations. If unspecified, the value for `STA_CORNERS` from the PDK will be used.",
338
+ "Resizer step-specific override for PNR_CORNERS.",
339
339
  ),
340
340
  ]
librelane/steps/cvc_rv.py CHANGED
@@ -165,5 +165,5 @@ class ERC(Step):
165
165
  )
166
166
  return {}, {}
167
167
  except CVCNoSupport as e:
168
- self.warn(f"Could not run CVC: {e}. Skipping…")
168
+ self.warn(f"Could not run CVC: {e}. Skipping '{self.id}'…")
169
169
  return {}, {}
@@ -29,6 +29,14 @@ from ..state import DesignFormat, State
29
29
  from ..common import Path, get_script_dir, mkdirp, _get_process_limit
30
30
 
31
31
 
32
+ DesignFormat(
33
+ "klayout_gds",
34
+ "klayout.gds",
35
+ "GDSII Stream (KLayout)",
36
+ alts=["KLAYOUT_GDS"],
37
+ ).register()
38
+
39
+
32
40
  class KLayoutStep(Step):
33
41
  config_vars = [
34
42
  Variable(
@@ -116,14 +124,14 @@ class KLayoutStep(Step):
116
124
  result += lef_args
117
125
 
118
126
  if include_gds:
119
- gds_args = []
127
+ gds_args: List[str] = []
120
128
  for gds in self.config["CELL_GDS"]:
121
129
  gds_args.append("--with-gds-file")
122
130
  gds_args.append(gds)
123
131
  for gds in self.toolbox.get_macro_views(self.config, DesignFormat.GDS):
124
132
  gds_args.append("--with-gds-file")
125
- gds_args.append(gds)
126
- if extra_gds := self.config["EXTRA_GDS_FILES"]:
133
+ gds_args.append(str(gds))
134
+ if extra_gds := self.config["EXTRA_GDS"]:
127
135
  for gds in extra_gds:
128
136
  gds_args.append("--with-gds-file")
129
137
  gds_args.append(gds)
@@ -149,7 +157,7 @@ class Render(KLayoutStep):
149
157
 
150
158
  def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
151
159
  input_view = state_in[DesignFormat.DEF]
152
- if gds := state_in[DesignFormat.GDS]:
160
+ if gds := state_in.get(DesignFormat.GDS):
153
161
  input_view = gds
154
162
 
155
163
  assert isinstance(input_view, Path)
@@ -193,7 +201,7 @@ class StreamOut(KLayoutStep):
193
201
 
194
202
  klayout_gds_out = os.path.join(
195
203
  self.step_dir,
196
- f"{self.config['DESIGN_NAME']}.{DesignFormat.KLAYOUT_GDS.value.extension}",
204
+ f"{self.config['DESIGN_NAME']}.{DesignFormat.KLAYOUT_GDS.extension}",
197
205
  )
198
206
  kwargs, env = self.extract_env(kwargs)
199
207
 
@@ -205,7 +213,7 @@ class StreamOut(KLayoutStep):
205
213
  "klayout",
206
214
  "stream_out.py",
207
215
  ),
208
- state_in[DesignFormat.DEF.value.id],
216
+ state_in[DesignFormat.DEF.id],
209
217
  "--output",
210
218
  abspath(klayout_gds_out),
211
219
  "--top",
librelane/steps/magic.py CHANGED
@@ -37,6 +37,22 @@ from ..config import Variable
37
37
  from ..common import get_script_dir, DRC as DRCObject, Path, mkdirp
38
38
 
39
39
 
40
+ DesignFormat(
41
+ "mag",
42
+ "mag",
43
+ "Magic VLSI View",
44
+ alts=["MAG"],
45
+ ).register()
46
+
47
+
48
+ DesignFormat(
49
+ "mag_gds",
50
+ "magic.gds",
51
+ "GDSII Stream (Magic)",
52
+ alts=["MAG_GDS"],
53
+ ).register()
54
+
55
+
40
56
  class MagicOutputProcessor(OutputProcessor):
41
57
  _error_patterns = [
42
58
  re.compile(rx)
@@ -192,10 +208,10 @@ class MagicStep(TclStep):
192
208
 
193
209
  views_updates: ViewsUpdate = {}
194
210
  for output in self.outputs:
195
- if output.value.multiple:
211
+ if output.multiple:
196
212
  # Too step-specific.
197
213
  continue
198
- path = Path(env[f"SAVE_{output.name}"])
214
+ path = Path(env[f"SAVE_{output.id.upper()}"])
199
215
  if not path.exists():
200
216
  continue
201
217
  views_updates[output] = path
librelane/steps/misc.py CHANGED
@@ -39,7 +39,7 @@ class LoadBaseSDC(Step):
39
39
  outputs = [DesignFormat.SDC]
40
40
 
41
41
  def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
42
- path = self.config["FALLBACK_SDC_FILE"]
42
+ path = self.config["FALLBACK_SDC"]
43
43
 
44
44
  target = os.path.join(self.step_dir, f"{self.config['DESIGN_NAME']}.sdc")
45
45
 
librelane/steps/odb.py CHANGED
@@ -76,9 +76,9 @@ class OdbpyStep(Step):
76
76
  views_updates: ViewsUpdate = {}
77
77
  command = self.get_command()
78
78
  for output in automatic_outputs:
79
- filename = f"{self.config['DESIGN_NAME']}.{output.value.extension}"
79
+ filename = f"{self.config['DESIGN_NAME']}.{output.extension}"
80
80
  file_path = os.path.join(self.step_dir, filename)
81
- command.append(f"--output-{output.value.id}")
81
+ command.append(f"--output-{output.id}")
82
82
  command.append(file_path)
83
83
  views_updates[output] = Path(file_path)
84
84
 
@@ -148,7 +148,7 @@ class OdbpyStep(Step):
148
148
  for lef in extra_lefs:
149
149
  lefs.append("--input-lef")
150
150
  lefs.append(lef)
151
- if (design_lef := self.state_in.result()[DesignFormat.LEF]) and (
151
+ if (design_lef := self.state_in.result().get(DesignFormat.LEF)) and (
152
152
  DesignFormat.LEF in self.inputs
153
153
  ):
154
154
  lefs.append("--design-lef")
@@ -207,7 +207,7 @@ class CheckMacroAntennaProperties(OdbpyStep):
207
207
 
208
208
  def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
209
209
  if not self.get_cells():
210
- info("No cells provided, skipping…")
210
+ info(f"No cells provided, skipping '{self.id}'…")
211
211
  return {}, {}
212
212
  return super().run(state_in, **kwargs)
213
213
 
@@ -271,7 +271,7 @@ class ApplyDEFTemplate(OdbpyStep):
271
271
 
272
272
  def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
273
273
  if self.config["FP_DEF_TEMPLATE"] is None:
274
- info("No DEF template provided, skipping…")
274
+ info(f"No DEF template provided, skipping '{self.id}'…")
275
275
  return {}, {}
276
276
 
277
277
  views_updates, metrics_updates = super().run(state_in, **kwargs)
@@ -527,9 +527,9 @@ class AddRoutingObstructions(OdbpyStep):
527
527
  config_vars = [
528
528
  Variable(
529
529
  "ROUTING_OBSTRUCTIONS",
530
- Optional[List[str]],
530
+ Optional[List[Tuple[str, Decimal, Decimal, Decimal, Decimal]]],
531
531
  "Add routing obstructions to the design. If set to `None`, this step is skipped."
532
- + " Format of each obstruction item is: layer llx lly urx ury.",
532
+ + " Format of each obstruction item is a tuple of: layer name, llx, lly, urx, ury.",
533
533
  units="µm",
534
534
  default=None,
535
535
  deprecated_names=["GRT_OBS"],
@@ -550,7 +550,7 @@ class AddRoutingObstructions(OdbpyStep):
550
550
  if obstructions := self.config[self.config_vars[0].name]:
551
551
  for obstruction in obstructions:
552
552
  command.append("--obstructions")
553
- command.append(obstruction)
553
+ command.append(" ".join([str(o) for o in obstruction]))
554
554
  return command
555
555
 
556
556
  def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
@@ -579,9 +579,9 @@ class AddPDNObstructions(AddRoutingObstructions):
579
579
  config_vars = [
580
580
  Variable(
581
581
  "PDN_OBSTRUCTIONS",
582
- Optional[List[str]],
582
+ Optional[List[Tuple[str, Decimal, Decimal, Decimal, Decimal]]],
583
583
  "Add routing obstructions to the design before PDN stage. If set to `None`, this step is skipped."
584
- + " Format of each obstruction item is: layer llx lly urx ury.",
584
+ + " Format of each obstruction item is a tuple of: layer name, llx, lly, urx, ury,.",
585
585
  units="µm",
586
586
  default=None,
587
587
  ),
@@ -686,7 +686,7 @@ class CustomIOPlacement(OdbpyStep):
686
686
 
687
687
  def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
688
688
  if self.config["FP_PIN_ORDER_CFG"] is None:
689
- info("No custom floorplan file configured, skipping…")
689
+ info(f"No custom floorplan file configured, skipping '{self.id}'…")
690
690
  return {}, {}
691
691
  return super().run(state_in, **kwargs)
692
692
 
@@ -715,7 +715,7 @@ class PortDiodePlacement(OdbpyStep):
715
715
  ),
716
716
  Variable(
717
717
  "GPL_CELL_PADDING",
718
- Decimal,
718
+ int,
719
719
  "Cell padding value (in sites) for global placement. Used by this step only to emit a warning if it's 0.",
720
720
  units="sites",
721
721
  pdk=True,
@@ -744,7 +744,11 @@ class PortDiodePlacement(OdbpyStep):
744
744
 
745
745
  def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
746
746
  if self.config["DIODE_ON_PORTS"] == "none":
747
- info("'DIODE_ON_PORTS' is set to 'none': skipping…")
747
+ info(f"'DIODE_ON_PORTS' is set to 'none': skipping '{self.id}'…")
748
+ return {}, {}
749
+
750
+ if self.config["DIODE_CELL"] is None:
751
+ info(f"'DIODE_CELL' not set. Skipping '{self.id}'…")
748
752
  return {}, {}
749
753
 
750
754
  if self.config["GPL_CELL_PADDING"] == 0:
@@ -784,7 +788,10 @@ class DiodesOnPorts(CompositeStep):
784
788
 
785
789
  def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
786
790
  if self.config["DIODE_ON_PORTS"] == "none":
787
- info("'DIODE_ON_PORTS' is set to 'none': skipping…")
791
+ info(f"'DIODE_ON_PORTS' is set to 'none': skipping '{self.id}'…")
792
+ return {}, {}
793
+ if self.config["DIODE_CELL"] is None:
794
+ info(f"'DIODE_CELL' not set. Skipping '{self.id}'…")
788
795
  return {}, {}
789
796
  return super().run(state_in, **kwargs)
790
797
 
@@ -811,14 +818,14 @@ class FuzzyDiodePlacement(OdbpyStep):
811
818
  config_vars = [
812
819
  Variable(
813
820
  "HEURISTIC_ANTENNA_THRESHOLD",
814
- Decimal,
821
+ Optional[Decimal],
815
822
  "A Manhattan distance above which a diode is recommended to be inserted by the heuristic inserter. If not specified, the heuristic algorithm.",
816
823
  units="µm",
817
824
  pdk=True,
818
825
  ),
819
826
  Variable(
820
827
  "GPL_CELL_PADDING",
821
- Decimal,
828
+ int,
822
829
  "Cell padding value (in sites) for global placement. Used by this step only to emit a warning if it's 0.",
823
830
  units="sites",
824
831
  pdk=True,
@@ -834,20 +841,14 @@ class FuzzyDiodePlacement(OdbpyStep):
834
841
  def get_command(self) -> List[str]:
835
842
  cell, pin = self.config["DIODE_CELL"].split("/")
836
843
 
837
- threshold_opts = []
838
- if threshold := self.config["HEURISTIC_ANTENNA_THRESHOLD"]:
839
- threshold_opts = ["--threshold", threshold]
840
-
841
- return (
842
- super().get_command()
843
- + [
844
- "--diode-cell",
845
- cell,
846
- "--diode-pin",
847
- pin,
848
- ]
849
- + threshold_opts
850
- )
844
+ return super().get_command() + [
845
+ "--diode-cell",
846
+ cell,
847
+ "--diode-pin",
848
+ pin,
849
+ "--threshold",
850
+ str(self.config["HEURISTIC_ANTENNA_THRESHOLD"]),
851
+ ]
851
852
 
852
853
  def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
853
854
  if self.config["GPL_CELL_PADDING"] == 0:
@@ -855,6 +856,14 @@ class FuzzyDiodePlacement(OdbpyStep):
855
856
  "'GPL_CELL_PADDING' is set to 0. This step may cause overlap failures."
856
857
  )
857
858
 
859
+ if self.config["DIODE_CELL"] is None:
860
+ info(f"'DIODE_CELL' not set. Skipping '{self.id}'…")
861
+ return {}, {}
862
+
863
+ if self.config["HEURISTIC_ANTENNA_THRESHOLD"] is None:
864
+ info(f"'HEURISTIC_ANTENNA_THRESHOLD' not set. Skipping '{self.id}'…")
865
+ return {}, {}
866
+
858
867
  return super().run(state_in, **kwargs)
859
868
 
860
869
 
@@ -889,6 +898,16 @@ class HeuristicDiodeInsertion(CompositeStep):
889
898
  GlobalRouting,
890
899
  ]
891
900
 
901
+ def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
902
+ if self.config["DIODE_CELL"] is None:
903
+ info(f"'DIODE_CELL' not set. Skipping '{self.id}'…")
904
+ return {}, {}
905
+ if self.config["HEURISTIC_ANTENNA_THRESHOLD"] is None:
906
+ info(f"'HEURISTIC_ANTENNA_THRESHOLD' not set. Skipping '{self.id}'…")
907
+ return {}, {}
908
+
909
+ return super().run(state_in, **kwargs)
910
+
892
911
 
893
912
  @Step.factory.register()
894
913
  class CellFrequencyTables(OdbpyStep):
@@ -975,7 +994,7 @@ class ManualGlobalPlacement(OdbpyStep):
975
994
 
976
995
  def run(self, state_in: State, **kwargs) -> Tuple[ViewsUpdate, MetricsUpdate]:
977
996
  if self.config["MANUAL_GLOBAL_PLACEMENTS"] is None:
978
- info("'MANUAL_GLOBAL_PLACEMENTS' not set, skipping…")
997
+ info(f"'MANUAL_GLOBAL_PLACEMENTS' not set, skipping '{self.id}'…")
979
998
  return {}, {}
980
999
  return super().run(state_in, **kwargs)
981
1000