siliconcompiler 0.35.3__py3-none-any.whl → 0.36.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.
Files changed (96) hide show
  1. siliconcompiler/_metadata.py +1 -1
  2. siliconcompiler/apps/sc_issue.py +18 -2
  3. siliconcompiler/checklist.py +2 -1
  4. siliconcompiler/constraints/__init__.py +4 -1
  5. siliconcompiler/constraints/asic_component.py +49 -11
  6. siliconcompiler/constraints/asic_floorplan.py +23 -21
  7. siliconcompiler/constraints/asic_pins.py +55 -17
  8. siliconcompiler/constraints/asic_timing.py +280 -57
  9. siliconcompiler/constraints/fpga_timing.py +212 -18
  10. siliconcompiler/constraints/timing_mode.py +82 -0
  11. siliconcompiler/data/templates/replay/replay.sh.j2 +27 -14
  12. siliconcompiler/data/templates/tcl/manifest.tcl.j2 +0 -6
  13. siliconcompiler/flowgraph.py +95 -42
  14. siliconcompiler/flows/generate_openroad_rcx.py +2 -2
  15. siliconcompiler/flows/highresscreenshotflow.py +37 -0
  16. siliconcompiler/library.py +2 -1
  17. siliconcompiler/package/__init__.py +56 -51
  18. siliconcompiler/project.py +13 -2
  19. siliconcompiler/scheduler/docker.py +24 -25
  20. siliconcompiler/scheduler/scheduler.py +143 -100
  21. siliconcompiler/scheduler/schedulernode.py +138 -22
  22. siliconcompiler/scheduler/slurm.py +120 -35
  23. siliconcompiler/scheduler/taskscheduler.py +19 -23
  24. siliconcompiler/schema/_metadata.py +1 -1
  25. siliconcompiler/schema/editableschema.py +29 -0
  26. siliconcompiler/schema/namedschema.py +2 -4
  27. siliconcompiler/schema/parametervalue.py +14 -2
  28. siliconcompiler/schema_support/cmdlineschema.py +0 -3
  29. siliconcompiler/schema_support/dependencyschema.py +0 -6
  30. siliconcompiler/schema_support/option.py +82 -1
  31. siliconcompiler/schema_support/pathschema.py +7 -13
  32. siliconcompiler/schema_support/record.py +4 -3
  33. siliconcompiler/tool.py +105 -52
  34. siliconcompiler/tools/_common/tcl/sc_schema_access.tcl +0 -6
  35. siliconcompiler/tools/keplerformal/__init__.py +7 -0
  36. siliconcompiler/tools/keplerformal/lec.py +112 -0
  37. siliconcompiler/tools/klayout/__init__.py +3 -0
  38. siliconcompiler/tools/klayout/screenshot.py +66 -1
  39. siliconcompiler/tools/klayout/scripts/klayout_convert_drc_db.py +1 -0
  40. siliconcompiler/tools/klayout/scripts/klayout_export.py +11 -40
  41. siliconcompiler/tools/klayout/scripts/klayout_operations.py +1 -0
  42. siliconcompiler/tools/klayout/scripts/klayout_show.py +5 -4
  43. siliconcompiler/tools/klayout/scripts/klayout_utils.py +16 -5
  44. siliconcompiler/tools/montage/tile.py +26 -12
  45. siliconcompiler/tools/openroad/__init__.py +27 -1
  46. siliconcompiler/tools/openroad/_apr.py +107 -14
  47. siliconcompiler/tools/openroad/clock_tree_synthesis.py +1 -0
  48. siliconcompiler/tools/openroad/global_placement.py +1 -0
  49. siliconcompiler/tools/openroad/init_floorplan.py +119 -7
  50. siliconcompiler/tools/openroad/power_grid_analysis.py +174 -0
  51. siliconcompiler/tools/openroad/repair_design.py +1 -0
  52. siliconcompiler/tools/openroad/repair_timing.py +1 -0
  53. siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +1 -1
  54. siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +91 -18
  55. siliconcompiler/tools/openroad/scripts/apr/sc_irdrop.tcl +148 -0
  56. siliconcompiler/tools/openroad/scripts/apr/sc_repair_design.tcl +1 -1
  57. siliconcompiler/tools/openroad/scripts/apr/sc_write_data.tcl +8 -10
  58. siliconcompiler/tools/openroad/scripts/common/procs.tcl +15 -6
  59. siliconcompiler/tools/openroad/scripts/common/read_liberty.tcl +2 -2
  60. siliconcompiler/tools/openroad/scripts/common/reports.tcl +7 -4
  61. siliconcompiler/tools/openroad/scripts/common/screenshot.tcl +1 -1
  62. siliconcompiler/tools/openroad/scripts/common/write_data_physical.tcl +8 -0
  63. siliconcompiler/tools/openroad/scripts/common/write_images.tcl +16 -12
  64. siliconcompiler/tools/openroad/scripts/rcx/sc_rcx_bench.tcl +2 -4
  65. siliconcompiler/tools/openroad/scripts/sc_rdlroute.tcl +3 -1
  66. siliconcompiler/tools/openroad/write_data.py +2 -2
  67. siliconcompiler/tools/opensta/__init__.py +1 -1
  68. siliconcompiler/tools/opensta/scripts/sc_check_library.tcl +2 -2
  69. siliconcompiler/tools/opensta/scripts/sc_report_libraries.tcl +2 -2
  70. siliconcompiler/tools/opensta/scripts/sc_timing.tcl +13 -10
  71. siliconcompiler/tools/opensta/timing.py +6 -2
  72. siliconcompiler/tools/vivado/scripts/sc_bitstream.tcl +11 -0
  73. siliconcompiler/tools/vivado/scripts/sc_place.tcl +11 -0
  74. siliconcompiler/tools/vivado/scripts/sc_route.tcl +11 -0
  75. siliconcompiler/tools/vivado/scripts/sc_syn_fpga.tcl +10 -0
  76. siliconcompiler/tools/vpr/__init__.py +28 -0
  77. siliconcompiler/tools/yosys/scripts/sc_screenshot.tcl +1 -1
  78. siliconcompiler/tools/yosys/scripts/sc_synth_asic.tcl +40 -4
  79. siliconcompiler/tools/yosys/scripts/sc_synth_fpga.tcl +15 -5
  80. siliconcompiler/tools/yosys/syn_asic.py +42 -0
  81. siliconcompiler/tools/yosys/syn_fpga.py +8 -0
  82. siliconcompiler/toolscripts/_tools.json +12 -7
  83. siliconcompiler/toolscripts/ubuntu22/install-keplerformal.sh +72 -0
  84. siliconcompiler/toolscripts/ubuntu24/install-keplerformal.sh +72 -0
  85. siliconcompiler/utils/__init__.py +243 -51
  86. siliconcompiler/utils/curation.py +89 -56
  87. siliconcompiler/utils/issue.py +6 -1
  88. siliconcompiler/utils/multiprocessing.py +46 -2
  89. siliconcompiler/utils/paths.py +21 -0
  90. siliconcompiler/utils/settings.py +162 -0
  91. {siliconcompiler-0.35.3.dist-info → siliconcompiler-0.36.0.dist-info}/METADATA +5 -4
  92. {siliconcompiler-0.35.3.dist-info → siliconcompiler-0.36.0.dist-info}/RECORD +96 -87
  93. {siliconcompiler-0.35.3.dist-info → siliconcompiler-0.36.0.dist-info}/WHEEL +0 -0
  94. {siliconcompiler-0.35.3.dist-info → siliconcompiler-0.36.0.dist-info}/entry_points.txt +0 -0
  95. {siliconcompiler-0.35.3.dist-info → siliconcompiler-0.36.0.dist-info}/licenses/LICENSE +0 -0
  96. {siliconcompiler-0.35.3.dist-info → siliconcompiler-0.36.0.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,10 @@
1
- from typing import Union, Set, List, Tuple
1
+ from typing import Union, Set, List, Tuple, Optional, Dict
2
2
 
3
3
  from siliconcompiler.schema import BaseSchema, NamedSchema, EditableSchema, Parameter, \
4
4
  PerNode, Scope
5
5
  from siliconcompiler import Design
6
+ from siliconcompiler.constraints.timing_mode import TimingModeSchema
7
+ from siliconcompiler.schema.baseschema import LazyLoad
6
8
 
7
9
 
8
10
  class ASICTimingScenarioSchema(NamedSchema):
@@ -14,7 +16,7 @@ class ASICTimingScenarioSchema(NamedSchema):
14
16
  operating mode, SDC filesets, and timing checks to be performed.
15
17
  """
16
18
 
17
- def __init__(self, name: str = None):
19
+ def __init__(self, name: Optional[str] = None):
18
20
  super().__init__()
19
21
  self.set_name(name)
20
22
 
@@ -93,20 +95,6 @@ class ASICTimingScenarioSchema(NamedSchema):
93
95
  help="""Operating mode for the scenario. Operating mode strings
94
96
  can be values such as test, functional, standby."""))
95
97
 
96
- schema.insert(
97
- 'sdcfileset',
98
- Parameter(
99
- '[(str,str)]',
100
- pernode=PerNode.OPTIONAL,
101
- scope=Scope.GLOBAL,
102
- shorthelp="Constraint: SDC files",
103
- switch="-constraint_timing_file 'scenario <file>'",
104
- example=["api: asic.set('constraint', 'timing', 'worst', 'file', 'hello.sdc')"],
105
- help="""List of timing constraint sets files to use for the scenario. The
106
- values are combined with any constraints specified by the design
107
- 'constraint' parameter. If no constraints are found, a default
108
- constraint file is used based on the clock definitions."""))
109
-
110
98
  schema.insert(
111
99
  'check',
112
100
  Parameter(
@@ -126,7 +114,7 @@ class ASICTimingScenarioSchema(NamedSchema):
126
114
  def set_pin_voltage(self,
127
115
  pin: str,
128
116
  voltage: float,
129
- step: str = None, index: Union[str, int] = None):
117
+ step: Optional[str] = None, index: Optional[Union[str, int]] = None):
130
118
  """
131
119
  Sets the voltage for a specified pin.
132
120
 
@@ -140,7 +128,8 @@ class ASICTimingScenarioSchema(NamedSchema):
140
128
 
141
129
  def get_pin_voltage(self,
142
130
  pin: str,
143
- step: str = None, index: Union[str, int] = None) -> float:
131
+ step: Optional[str] = None, index: Optional[Union[str, int]] = None) \
132
+ -> float:
144
133
  """
145
134
  Gets the voltage of a specified pin.
146
135
 
@@ -162,7 +151,7 @@ class ASICTimingScenarioSchema(NamedSchema):
162
151
  def add_libcorner(self,
163
152
  libcorner: Union[List[str], str],
164
153
  clobber: bool = False,
165
- step: str = None, index: Union[str, int] = None):
154
+ step: Optional[str] = None, index: Optional[Union[str, int]] = None):
166
155
  """
167
156
  Adds a library corner to the design.
168
157
 
@@ -179,8 +168,8 @@ class ASICTimingScenarioSchema(NamedSchema):
179
168
  else:
180
169
  return self.add("libcorner", libcorner, step=step, index=index)
181
170
 
182
- def get_libcorner(self,
183
- step: str = None, index: Union[str, int] = None) -> Set[str]:
171
+ def get_libcorner(self, step: Optional[str] = None, index: Optional[Union[str, int]] = None) \
172
+ -> Set[str]:
184
173
  """
185
174
  Gets the set of library corners.
186
175
 
@@ -195,7 +184,7 @@ class ASICTimingScenarioSchema(NamedSchema):
195
184
 
196
185
  def set_pexcorner(self,
197
186
  pexcorner: str,
198
- step: str = None, index: Union[str, int] = None):
187
+ step: Optional[str] = None, index: Optional[Union[str, int]] = None):
199
188
  """
200
189
  Sets the parasitic extraction (PEX) corner for the design.
201
190
 
@@ -207,7 +196,7 @@ class ASICTimingScenarioSchema(NamedSchema):
207
196
  return self.set("pexcorner", pexcorner, step=step, index=index)
208
197
 
209
198
  def get_pexcorner(self,
210
- step: str = None, index: Union[str, int] = None) -> str:
199
+ step: Optional[str] = None, index: Optional[Union[str, int]] = None) -> str:
211
200
  """
212
201
  Gets the parasitic extraction (PEX) corner currently set for the design.
213
202
 
@@ -222,7 +211,7 @@ class ASICTimingScenarioSchema(NamedSchema):
222
211
 
223
212
  def set_mode(self,
224
213
  mode: str,
225
- step: str = None, index: Union[str, int] = None):
214
+ step: Optional[str] = None, index: Optional[Union[str, int]] = None):
226
215
  """
227
216
  Sets the operational mode for the design.
228
217
 
@@ -234,7 +223,7 @@ class ASICTimingScenarioSchema(NamedSchema):
234
223
  return self.set("mode", mode, step=step, index=index)
235
224
 
236
225
  def get_mode(self,
237
- step: str = None, index: Union[str, int] = None) -> str:
226
+ step: Optional[str] = None, index: Optional[Union[str, int]] = None) -> str:
238
227
  """
239
228
  Gets the operational mode currently set for the design.
240
229
 
@@ -249,7 +238,7 @@ class ASICTimingScenarioSchema(NamedSchema):
249
238
 
250
239
  def set_opcond(self,
251
240
  opcond: str,
252
- step: str = None, index: Union[str, int] = None):
241
+ step: Optional[str] = None, index: Optional[Union[str, int]] = None):
253
242
  """
254
243
  Sets the operating condition for the design.
255
244
 
@@ -261,7 +250,7 @@ class ASICTimingScenarioSchema(NamedSchema):
261
250
  return self.set("opcond", opcond, step=step, index=index)
262
251
 
263
252
  def get_opcond(self,
264
- step: str = None, index: Union[str, int] = None) -> str:
253
+ step: Optional[str] = None, index: Optional[Union[str, int]] = None) -> str:
265
254
  """
266
255
  Gets the operating condition currently set for the design.
267
256
 
@@ -276,7 +265,7 @@ class ASICTimingScenarioSchema(NamedSchema):
276
265
 
277
266
  def set_temperature(self,
278
267
  temperature: float,
279
- step: str = None, index: Union[str, int] = None):
268
+ step: Optional[str] = None, index: Optional[Union[str, int]] = None):
280
269
  """
281
270
  Sets the temperature for the design.
282
271
 
@@ -287,8 +276,8 @@ class ASICTimingScenarioSchema(NamedSchema):
287
276
  """
288
277
  return self.set("temperature", temperature, step=step, index=index)
289
278
 
290
- def get_temperature(self,
291
- step: str = None, index: Union[str, int] = None) -> float:
279
+ def get_temperature(self, step: Optional[str] = None, index: Optional[Union[str, int]] = None) \
280
+ -> float:
292
281
  """
293
282
  Gets the temperature currently set for the design.
294
283
 
@@ -305,7 +294,7 @@ class ASICTimingScenarioSchema(NamedSchema):
305
294
  design: Union[Design, str],
306
295
  fileset: str,
307
296
  clobber: bool = False,
308
- step: str = None, index: Union[str, int] = None):
297
+ step: Optional[str] = None, index: Optional[Union[str, int]] = None):
309
298
  """
310
299
  Adds an SDC fileset for a given design.
311
300
 
@@ -323,22 +312,25 @@ class ASICTimingScenarioSchema(NamedSchema):
323
312
  TypeError: If `design` is not a Design object or a string, or if `fileset` is not
324
313
  a string.
325
314
  """
326
- if isinstance(design, Design):
327
- design = design.name
328
-
329
- if not isinstance(design, str):
330
- raise TypeError("design must be a design object or string")
315
+ import warnings
316
+ warnings.warn("This function is deprecated and will be removed in a future version, "
317
+ "use TimingModeSchema instead", DeprecationWarning, stacklevel=2)
331
318
 
332
- if not isinstance(fileset, str):
333
- raise TypeError("fileset must be a string")
319
+ mode = self.get_mode(step=step, index=index)
320
+ if mode is None:
321
+ raise ValueError("Mode not defined")
334
322
 
335
- if clobber:
336
- return self.set("sdcfileset", (design, fileset), step=step, index=index)
337
- else:
338
- return self.add("sdcfileset", (design, fileset), step=step, index=index)
323
+ timing_constraints: ASICTimingConstraintSchema = self._parent()._parent()
324
+ try:
325
+ modeobj = timing_constraints.get_mode(mode)
326
+ except LookupError:
327
+ modeobj = timing_constraints.make_mode(mode)
328
+ return modeobj.add_sdcfileset(design=design, fileset=fileset,
329
+ clobber=clobber,
330
+ step=step, index=index)
339
331
 
340
- def get_sdcfileset(self,
341
- step: str = None, index: Union[str, int] = None) -> List[Tuple[str, str]]:
332
+ def get_sdcfileset(self, step: Optional[str] = None, index: Optional[Union[str, int]] = None) \
333
+ -> List[Tuple[str, str]]:
342
334
  """
343
335
  Gets the list of SDC filesets.
344
336
 
@@ -349,12 +341,25 @@ class ASICTimingScenarioSchema(NamedSchema):
349
341
  Returns:
350
342
  A list of tuples, where each tuple contains the design name and the SDC fileset name.
351
343
  """
352
- return self.get("sdcfileset", step=step, index=index)
344
+ import warnings
345
+ warnings.warn("This function is deprecated and will be removed in a future version, "
346
+ "use TimingModeSchema instead", DeprecationWarning, stacklevel=2)
347
+
348
+ mode = self.get_mode(step=step, index=index)
349
+ if mode is None:
350
+ raise ValueError("Mode not defined")
351
+
352
+ timing_constraints: ASICTimingConstraintSchema = self._parent()._parent()
353
+ try:
354
+ modeobj = timing_constraints.get_mode(mode)
355
+ except LookupError:
356
+ modeobj = timing_constraints.make_mode(mode)
357
+ return modeobj.get_sdcfileset(step=step, index=index)
353
358
 
354
359
  def add_check(self,
355
360
  check: Union[List[str], str],
356
361
  clobber: bool = False,
357
- step: str = None, index: Union[str, int] = None):
362
+ step: Optional[str] = None, index: Optional[Union[str, int]] = None):
358
363
  """
359
364
  Adds a check to the design process.
360
365
 
@@ -371,7 +376,8 @@ class ASICTimingScenarioSchema(NamedSchema):
371
376
  else:
372
377
  return self.add("check", check, step=step, index=index)
373
378
 
374
- def get_check(self, step: str = None, index: Union[str, int] = None) -> Set[str]:
379
+ def get_check(self, step: Optional[str] = None, index: Optional[Union[str, int]] = None) \
380
+ -> Set[str]:
375
381
  """
376
382
  Gets the set of checks configured for the design process.
377
383
 
@@ -384,6 +390,30 @@ class ASICTimingScenarioSchema(NamedSchema):
384
390
  """
385
391
  return self.get("check", step=step, index=index)
386
392
 
393
+ def _from_dict(self, manifest: Dict,
394
+ keypath: Union[List[str], Tuple[str, ...]],
395
+ version: Optional[Tuple[int, ...]] = None,
396
+ lazyload: LazyLoad = LazyLoad.ON) \
397
+ -> Tuple[Set[Tuple[str, ...]], Set[Tuple[str, ...]]]:
398
+
399
+ sdcfileset = None
400
+ if version and version < (0, 53, 0):
401
+ sdcfileset = manifest.pop("sdcfileset", None)
402
+ lazyload = LazyLoad.OFF
403
+
404
+ ret = super()._from_dict(manifest, keypath, version, lazyload)
405
+
406
+ if sdcfileset:
407
+ param = Parameter.from_dict(sdcfileset, keypath=(*keypath, "sdcfileset"),
408
+ version=version)
409
+ for value, step, index in param.getvalues():
410
+ if self.get_mode(step=step, index=index) is None:
411
+ self.set_mode("_importcreated_", step=step, index=index)
412
+ for design, fileset in value:
413
+ self.add_sdcfileset(design, fileset, step=step, index=index)
414
+
415
+ return ret
416
+
387
417
 
388
418
  class ASICTimingConstraintSchema(BaseSchema):
389
419
  """
@@ -398,7 +428,8 @@ class ASICTimingConstraintSchema(BaseSchema):
398
428
  def __init__(self):
399
429
  super().__init__()
400
430
 
401
- EditableSchema(self).insert("default", ASICTimingScenarioSchema())
431
+ EditableSchema(self).insert("scenario", "default", ASICTimingScenarioSchema())
432
+ EditableSchema(self).insert("mode", "default", TimingModeSchema())
402
433
 
403
434
  def add_scenario(self, scenario: ASICTimingScenarioSchema):
404
435
  """
@@ -424,9 +455,10 @@ class ASICTimingConstraintSchema(BaseSchema):
424
455
  if scenario.name is None:
425
456
  raise ValueError("scenario must have a name")
426
457
 
427
- EditableSchema(self).insert(scenario.name, scenario, clobber=True)
458
+ EditableSchema(self).insert("scenario", scenario.name, scenario, clobber=True)
428
459
 
429
- def get_scenario(self, scenario: str = None):
460
+ def get_scenario(self, scenario: Optional[str] = None) \
461
+ -> Union[ASICTimingScenarioSchema, Dict[str, ASICTimingScenarioSchema]]:
430
462
  """
431
463
  Retrieves one or all timing scenarios from the configuration.
432
464
 
@@ -450,13 +482,13 @@ class ASICTimingConstraintSchema(BaseSchema):
450
482
  """
451
483
  if scenario is None:
452
484
  scenarios = {}
453
- for name in self.getkeys():
454
- scenarios[name] = self.get(name, field="schema")
485
+ for name in self.getkeys("scenario"):
486
+ scenarios[name] = self.get("scenario", name, field="schema")
455
487
  return scenarios
456
488
 
457
- if not self.valid(scenario):
489
+ if not self.valid("scenario", scenario):
458
490
  raise LookupError(f"{scenario} is not defined")
459
- return self.get(scenario, field="schema")
491
+ return self.get("scenario", scenario, field="schema")
460
492
 
461
493
  def make_scenario(self, scenario: str) -> ASICTimingScenarioSchema:
462
494
  """
@@ -483,13 +515,42 @@ class ASICTimingConstraintSchema(BaseSchema):
483
515
  if not scenario:
484
516
  raise ValueError("scenario name is required")
485
517
 
486
- if self.valid(scenario):
518
+ if self.valid("scenario", scenario):
487
519
  raise LookupError(f"{scenario} scenario already exists")
488
520
 
489
521
  scenarioobj = ASICTimingScenarioSchema(scenario)
490
522
  self.add_scenario(scenarioobj)
491
523
  return scenarioobj
492
524
 
525
+ def copy_scenario(self, scenario: str, name: str, insert: bool = True) \
526
+ -> ASICTimingScenarioSchema:
527
+ """
528
+ Copies an existing timing scenario, renames it, and optionally adds it to the design.
529
+
530
+ This method retrieves the scenario identified by ``scenario``, creates a
531
+ deep copy of it, and renames the copy to ``name``. If ``insert`` is True,
532
+ the new scenario is immediately added to the configuration.
533
+
534
+ Args:
535
+ scenario (str): The name of the existing scenario to be copied.
536
+ name (str): The name to assign to the new copied scenario.
537
+ insert (bool, optional): Whether to add the newly created scenario
538
+ to the configuration. Defaults to True.
539
+
540
+ Returns:
541
+ ASICTimingScenarioSchema: The newly created copy of the scenario.
542
+
543
+ Raises:
544
+ LookupError: If the source scenario specified by ``scenario`` does not exist.
545
+ """
546
+ constraint = EditableSchema(self.get_scenario(scenario)).copy()
547
+ EditableSchema(constraint).rename(name)
548
+ if insert:
549
+ if self.valid("scenario", name):
550
+ raise ValueError(f"{name} already exists")
551
+ self.add_scenario(constraint)
552
+ return constraint
553
+
493
554
  def remove_scenario(self, scenario: str) -> bool:
494
555
  """
495
556
  Removes a timing scenario from the design configuration.
@@ -511,8 +572,170 @@ class ASICTimingConstraintSchema(BaseSchema):
511
572
  if not scenario:
512
573
  raise ValueError("scenario name is required")
513
574
 
514
- if not self.valid(scenario):
575
+ if not self.valid("scenario", scenario):
576
+ return False
577
+
578
+ EditableSchema(self).remove("scenario", scenario)
579
+ return True
580
+
581
+ def add_mode(self, mode: TimingModeSchema):
582
+ """
583
+ Adds a timing mode to the design configuration.
584
+
585
+ This method is responsible for incorporating a new or updated timing mode
586
+ into the system's configuration. If a mode with the same name already
587
+ exists, it will be overwritten (`clobber=True`).
588
+
589
+ Args:
590
+ mode: The :class:`TimingModeSchema` object representing the timing mode
591
+ to add. This object must have a valid name defined via its `name()` method.
592
+
593
+ Raises:
594
+ TypeError: If the provided `mode` argument is not an instance of
595
+ :class:`TimingModeSchema`.
596
+ ValueError: If the `mode` object's `name()` method returns None, indicating
597
+ that the mode does not have a defined name.
598
+ """
599
+ if not isinstance(mode, TimingModeSchema):
600
+ raise TypeError("mode must be a timing mode object")
601
+
602
+ if mode.name is None:
603
+ raise ValueError("mode must have a name")
604
+
605
+ EditableSchema(self).insert("mode", mode.name, mode, clobber=True)
606
+
607
+ def get_mode(self, mode: Optional[str] = None) \
608
+ -> Union[TimingModeSchema, Dict[str, TimingModeSchema]]:
609
+ """
610
+ Retrieves one or all timing modes from the configuration.
611
+
612
+ This method provides flexibility to fetch either a specific timing mode
613
+ by its name or a collection of all currently defined modes.
614
+
615
+ Args:
616
+ mode (str, optional): The name (string) of the specific timing mode to retrieve.
617
+ If this argument is omitted or set to None, the method will
618
+ return a dictionary containing all available timing modes.
619
+
620
+ Returns:
621
+ If `mode` is provided: The :class:`TimingModeSchema` object corresponding
622
+ to the specified mode name.
623
+ If `mode` is None: A dictionary where keys are mode names (str) and
624
+ values are their respective :class:`TimingModeSchema` objects.
625
+
626
+ Raises:
627
+ LookupError: If a specific `mode` name is provided but no mode with
628
+ that name is found in the configuration.
629
+ """
630
+ if mode is None:
631
+ modes = {}
632
+ for name in self.getkeys("mode"):
633
+ modes[name] = self.get("mode", name, field="schema")
634
+ return modes
635
+
636
+ if not self.valid("mode", mode):
637
+ raise LookupError(f"{mode} is not defined")
638
+ return self.get("mode", mode, field="schema")
639
+
640
+ def make_mode(self, mode: str) -> TimingModeSchema:
641
+ """
642
+ Creates and adds a new timing mode with the specified name.
643
+
644
+ This method initializes a new :class:`TimingModeSchema` object with the given
645
+ name and immediately adds it to the constraint configuration. It ensures that
646
+ a mode with the same name does not already exist, preventing accidental
647
+ overwrites.
648
+
649
+ Args:
650
+ mode (str): The name for the new timing mode. This name must be
651
+ a non-empty string and unique within the current configuration.
652
+
653
+ Returns:
654
+ :class:`TimingModeSchema`: The newly created :class:`TimingModeSchema`
655
+ object.
656
+
657
+ Raises:
658
+ ValueError: If the provided `mode` name is empty or None.
659
+ LookupError: If a mode with the specified `mode` name already exists
660
+ in the configuration.
661
+ """
662
+ if not mode:
663
+ raise ValueError("mode name is required")
664
+
665
+ if self.valid("mode", mode):
666
+ raise LookupError(f"{mode} mode already exists")
667
+
668
+ modeobj = TimingModeSchema(mode)
669
+ self.add_mode(modeobj)
670
+ return modeobj
671
+
672
+ def copy_mode(self, mode: str, name: str, insert: bool = True) \
673
+ -> TimingModeSchema:
674
+ """
675
+ Copies an existing timing mode, renames it, and optionally adds it to the design.
676
+
677
+ This method retrieves the mode identified by ``mode``, creates a
678
+ deep copy of it, and renames the copy to ``name``. If ``insert`` is True,
679
+ the new mode is immediately added to the configuration.
680
+
681
+ Args:
682
+ mode (str): The name of the existing mode to be copied.
683
+ name (str): The name to assign to the new copied mode.
684
+ insert (bool, optional): Whether to add the newly created mode
685
+ to the configuration. Defaults to True.
686
+
687
+ Returns:
688
+ TimingModeSchema: The newly created copy of the mode.
689
+
690
+ Raises:
691
+ LookupError: If the source mode specified by ``mode`` does not exist.
692
+ """
693
+ newmode = EditableSchema(self.get_mode(mode)).copy()
694
+ EditableSchema(newmode).rename(name)
695
+ if insert:
696
+ if self.valid("mode", name):
697
+ raise ValueError(f"{name} already exists")
698
+ self.add_mode(newmode)
699
+ return newmode
700
+
701
+ def remove_mode(self, mode: str) -> bool:
702
+ """
703
+ Removes a timing mode from the design configuration.
704
+
705
+ This method deletes the specified timing mode from the system's
706
+ configuration.
707
+
708
+ Args:
709
+ mode (str): The name of the timing mode to remove.
710
+ This name must be a non-empty string.
711
+
712
+ Returns:
713
+ bool: True if the mode was successfully removed, False if no
714
+ mode with the given name was found.
715
+
716
+ Raises:
717
+ ValueError: If the provided `mode` name is empty or None.
718
+ """
719
+ if not mode:
720
+ raise ValueError("mode name is required")
721
+
722
+ if not self.valid("mode", mode):
515
723
  return False
516
724
 
517
- EditableSchema(self).remove(scenario)
725
+ EditableSchema(self).remove("mode", mode)
518
726
  return True
727
+
728
+ def _from_dict(self, manifest: Dict,
729
+ keypath: Union[List[str], Tuple[str, ...]],
730
+ version: Optional[Tuple[int, ...]] = None,
731
+ lazyload: LazyLoad = LazyLoad.ON) \
732
+ -> Tuple[Set[Tuple[str, ...]], Set[Tuple[str, ...]]]:
733
+ if version and version < (0, 53, 0):
734
+ manifest.pop("__meta__", None)
735
+ manifest = {
736
+ "scenario": manifest,
737
+ "mode": self.getdict("mode")
738
+ }
739
+ lazyload = LazyLoad.OFF
740
+
741
+ return super()._from_dict(manifest, keypath, version, lazyload)