siliconcompiler 0.34.1__py3-none-any.whl → 0.34.3__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 (129) hide show
  1. siliconcompiler/__init__.py +23 -4
  2. siliconcompiler/__main__.py +1 -7
  3. siliconcompiler/_metadata.py +1 -1
  4. siliconcompiler/apps/_common.py +104 -23
  5. siliconcompiler/apps/sc.py +4 -8
  6. siliconcompiler/apps/sc_dashboard.py +6 -4
  7. siliconcompiler/apps/sc_install.py +10 -6
  8. siliconcompiler/apps/sc_issue.py +7 -5
  9. siliconcompiler/apps/sc_remote.py +1 -1
  10. siliconcompiler/apps/sc_server.py +9 -14
  11. siliconcompiler/apps/sc_show.py +7 -6
  12. siliconcompiler/apps/smake.py +130 -94
  13. siliconcompiler/apps/utils/replay.py +4 -7
  14. siliconcompiler/apps/utils/summarize.py +3 -5
  15. siliconcompiler/asic.py +420 -0
  16. siliconcompiler/checklist.py +25 -2
  17. siliconcompiler/cmdlineschema.py +534 -0
  18. siliconcompiler/constraints/__init__.py +17 -0
  19. siliconcompiler/constraints/asic_component.py +378 -0
  20. siliconcompiler/constraints/asic_floorplan.py +449 -0
  21. siliconcompiler/constraints/asic_pins.py +489 -0
  22. siliconcompiler/constraints/asic_timing.py +517 -0
  23. siliconcompiler/core.py +10 -35
  24. siliconcompiler/data/templates/tcl/manifest.tcl.j2 +8 -0
  25. siliconcompiler/dependencyschema.py +96 -202
  26. siliconcompiler/design.py +327 -241
  27. siliconcompiler/filesetschema.py +250 -0
  28. siliconcompiler/flowgraph.py +298 -106
  29. siliconcompiler/fpga.py +124 -1
  30. siliconcompiler/library.py +331 -0
  31. siliconcompiler/metric.py +327 -92
  32. siliconcompiler/metrics/__init__.py +7 -0
  33. siliconcompiler/metrics/asic.py +245 -0
  34. siliconcompiler/metrics/fpga.py +220 -0
  35. siliconcompiler/package/__init__.py +391 -67
  36. siliconcompiler/package/git.py +92 -16
  37. siliconcompiler/package/github.py +114 -22
  38. siliconcompiler/package/https.py +79 -16
  39. siliconcompiler/packageschema.py +341 -16
  40. siliconcompiler/pathschema.py +255 -0
  41. siliconcompiler/pdk.py +566 -1
  42. siliconcompiler/project.py +1460 -0
  43. siliconcompiler/record.py +38 -1
  44. siliconcompiler/remote/__init__.py +5 -2
  45. siliconcompiler/remote/client.py +11 -6
  46. siliconcompiler/remote/schema.py +5 -23
  47. siliconcompiler/remote/server.py +41 -54
  48. siliconcompiler/report/__init__.py +3 -3
  49. siliconcompiler/report/dashboard/__init__.py +48 -14
  50. siliconcompiler/report/dashboard/cli/__init__.py +99 -21
  51. siliconcompiler/report/dashboard/cli/board.py +364 -179
  52. siliconcompiler/report/dashboard/web/__init__.py +90 -12
  53. siliconcompiler/report/dashboard/web/components/__init__.py +219 -240
  54. siliconcompiler/report/dashboard/web/components/flowgraph.py +49 -26
  55. siliconcompiler/report/dashboard/web/components/graph.py +139 -100
  56. siliconcompiler/report/dashboard/web/layouts/__init__.py +29 -1
  57. siliconcompiler/report/dashboard/web/layouts/_common.py +38 -2
  58. siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph.py +39 -26
  59. siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph_node_tab.py +50 -50
  60. siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph_sac_tabs.py +49 -46
  61. siliconcompiler/report/dashboard/web/state.py +141 -14
  62. siliconcompiler/report/dashboard/web/utils/__init__.py +79 -16
  63. siliconcompiler/report/dashboard/web/utils/file_utils.py +74 -11
  64. siliconcompiler/report/dashboard/web/viewer.py +25 -1
  65. siliconcompiler/report/report.py +5 -2
  66. siliconcompiler/report/summary_image.py +29 -11
  67. siliconcompiler/scheduler/__init__.py +9 -1
  68. siliconcompiler/scheduler/docker.py +81 -4
  69. siliconcompiler/scheduler/run_node.py +37 -20
  70. siliconcompiler/scheduler/scheduler.py +211 -36
  71. siliconcompiler/scheduler/schedulernode.py +394 -60
  72. siliconcompiler/scheduler/send_messages.py +77 -29
  73. siliconcompiler/scheduler/slurm.py +76 -12
  74. siliconcompiler/scheduler/taskscheduler.py +142 -21
  75. siliconcompiler/schema/__init__.py +0 -4
  76. siliconcompiler/schema/baseschema.py +338 -59
  77. siliconcompiler/schema/editableschema.py +14 -6
  78. siliconcompiler/schema/journal.py +28 -17
  79. siliconcompiler/schema/namedschema.py +22 -14
  80. siliconcompiler/schema/parameter.py +89 -28
  81. siliconcompiler/schema/parametertype.py +2 -0
  82. siliconcompiler/schema/parametervalue.py +258 -15
  83. siliconcompiler/schema/safeschema.py +25 -2
  84. siliconcompiler/schema/schema_cfg.py +23 -19
  85. siliconcompiler/schema/utils.py +2 -2
  86. siliconcompiler/schema_obj.py +24 -5
  87. siliconcompiler/tool.py +1131 -265
  88. siliconcompiler/tools/bambu/__init__.py +41 -0
  89. siliconcompiler/tools/builtin/concatenate.py +2 -2
  90. siliconcompiler/tools/builtin/minimum.py +2 -1
  91. siliconcompiler/tools/builtin/mux.py +2 -1
  92. siliconcompiler/tools/builtin/nop.py +2 -1
  93. siliconcompiler/tools/builtin/verify.py +2 -1
  94. siliconcompiler/tools/klayout/__init__.py +95 -0
  95. siliconcompiler/tools/openroad/__init__.py +289 -0
  96. siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +3 -0
  97. siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +7 -2
  98. siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +8 -4
  99. siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +9 -5
  100. siliconcompiler/tools/openroad/scripts/common/write_images.tcl +5 -1
  101. siliconcompiler/tools/slang/__init__.py +1 -1
  102. siliconcompiler/tools/slang/elaborate.py +2 -1
  103. siliconcompiler/tools/vivado/scripts/sc_run.tcl +1 -1
  104. siliconcompiler/tools/vivado/scripts/sc_syn_fpga.tcl +8 -1
  105. siliconcompiler/tools/vivado/syn_fpga.py +6 -0
  106. siliconcompiler/tools/vivado/vivado.py +35 -2
  107. siliconcompiler/tools/vpr/__init__.py +150 -0
  108. siliconcompiler/tools/yosys/__init__.py +369 -1
  109. siliconcompiler/tools/yosys/scripts/procs.tcl +0 -1
  110. siliconcompiler/toolscripts/_tools.json +5 -10
  111. siliconcompiler/utils/__init__.py +66 -0
  112. siliconcompiler/utils/flowgraph.py +2 -2
  113. siliconcompiler/utils/issue.py +2 -1
  114. siliconcompiler/utils/logging.py +14 -0
  115. siliconcompiler/utils/multiprocessing.py +256 -0
  116. siliconcompiler/utils/showtools.py +10 -0
  117. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.3.dist-info}/METADATA +6 -6
  118. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.3.dist-info}/RECORD +122 -115
  119. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.3.dist-info}/entry_points.txt +3 -0
  120. siliconcompiler/schema/cmdlineschema.py +0 -250
  121. siliconcompiler/schema/packageschema.py +0 -101
  122. siliconcompiler/toolscripts/rhel8/install-slang.sh +0 -40
  123. siliconcompiler/toolscripts/rhel9/install-slang.sh +0 -40
  124. siliconcompiler/toolscripts/ubuntu20/install-slang.sh +0 -47
  125. siliconcompiler/toolscripts/ubuntu22/install-slang.sh +0 -37
  126. siliconcompiler/toolscripts/ubuntu24/install-slang.sh +0 -37
  127. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.3.dist-info}/WHEEL +0 -0
  128. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.3.dist-info}/licenses/LICENSE +0 -0
  129. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.3.dist-info}/top_level.txt +0 -0
siliconcompiler/design.py CHANGED
@@ -1,13 +1,13 @@
1
- import contextlib
2
1
  import re
3
2
 
4
3
  import os.path
5
4
 
6
- from pathlib import Path
7
- from typing import List
5
+ from typing import List, Union, Tuple, Dict
8
6
 
9
7
  from siliconcompiler import utils
10
8
 
9
+ from siliconcompiler import LibrarySchema
10
+
11
11
  from siliconcompiler.dependencyschema import DependencySchema
12
12
  from siliconcompiler.schema import NamedSchema
13
13
  from siliconcompiler.schema import EditableSchema, Parameter, Scope
@@ -15,15 +15,54 @@ from siliconcompiler.schema.utils import trim
15
15
 
16
16
 
17
17
  ###########################################################################
18
- class DesignSchema(NamedSchema, DependencySchema):
18
+ class DesignSchema(LibrarySchema, DependencySchema):
19
+ '''
20
+ Schema for a 'design', which is a chip object that can be compiled.
21
+
22
+ This class inherits from :class:`~siliconcompiler.LibrarySchema` and
23
+ :class:`~siliconcompiler.DependencySchema`, and adds parameters and methods
24
+ specific to describing a design, such as its top module, source filesets,
25
+ and compilation settings.
26
+ '''
19
27
 
20
28
  def __init__(self, name: str = None):
29
+ '''
30
+ Initializes a new DesignSchema object.
31
+
32
+ Args:
33
+ name (str, optional): The name of the design. Defaults to None.
34
+ '''
21
35
  super().__init__()
22
36
  self.set_name(name)
23
37
 
24
38
  schema_design(self)
25
39
 
26
- self.__fileset = None
40
+ def add_dep(self, obj: NamedSchema, clobber: bool = True) -> bool:
41
+ '''
42
+ Adds a module dependency to this design.
43
+
44
+ This method extends the base `add_dep` to prevent a design from
45
+ adding a dependency on itself.
46
+
47
+ Args:
48
+ obj (NamedSchema): The dependency object to add.
49
+ clobber (bool): If True, overwrite an existing dependency with the
50
+ same name.
51
+
52
+ Returns:
53
+ bool: True if the dependency was added, False otherwise.
54
+
55
+ Raises:
56
+ TypeError: If `obj` is not a `NamedSchema`.
57
+ ValueError: If `obj` has the same name as the current design.
58
+ '''
59
+ if not isinstance(obj, NamedSchema):
60
+ raise TypeError(f"Cannot add an object of type: {type(obj)}")
61
+
62
+ if obj.name == self.name:
63
+ raise ValueError("Cannot add a dependency with the same name")
64
+
65
+ return super().add_dep(obj, clobber=clobber)
27
66
 
28
67
  ############################################
29
68
  def set_topmodule(self,
@@ -33,7 +72,8 @@ class DesignSchema(NamedSchema, DependencySchema):
33
72
 
34
73
  Args:
35
74
  value (str): Topmodule name.
36
- fileset (str, optional): Fileset name.
75
+ fileset (str, optional): Fileset name. If not provided, the active
76
+ fileset is used.
37
77
 
38
78
  Returns:
39
79
  str: Topmodule name
@@ -54,7 +94,8 @@ class DesignSchema(NamedSchema, DependencySchema):
54
94
  """Returns the topmodule of a fileset.
55
95
 
56
96
  Args:
57
- fileset (str): Fileset name.
97
+ fileset (str): Fileset name. If not provided, the active fileset is
98
+ used.
58
99
 
59
100
  Returns:
60
101
  str: Topmodule name
@@ -66,31 +107,33 @@ class DesignSchema(NamedSchema, DependencySchema):
66
107
  value: str,
67
108
  fileset: str = None,
68
109
  clobber: bool = False,
69
- package: str = None) -> List[str]:
110
+ dataroot: str = None) -> List[str]:
70
111
  """Adds include directories to a fileset.
71
112
 
72
113
  Args:
73
114
  value (str or Path): Include directory name.
74
- fileset (str, optional): Fileset name.
75
- clobber (bool, optional): Clears existing list before adding item
76
- package (str, optional): Package name
115
+ fileset (str, optional): Fileset name. If not provided, the active
116
+ fileset is used.
117
+ clobber (bool, optional): Clears existing list before adding item.
118
+ dataroot (str, optional): Data directory reference name.
77
119
 
78
120
  Returns:
79
121
  list[str]: List of include directories
80
122
  """
81
123
  return self.__set_add(fileset, 'idir', value, clobber, typelist=[str, list],
82
- package=package)
124
+ dataroot=dataroot)
83
125
 
84
126
  def get_idir(self, fileset: str = None) -> List[str]:
85
127
  """Returns include directories for a fileset.
86
128
 
87
129
  Args:
88
- fileset (str): Fileset name.
130
+ fileset (str): Fileset name. If not provided, the active fileset is
131
+ used.
89
132
 
90
133
  Returns:
91
134
  list[str]: List of include directories
92
135
  """
93
- return self.__get(fileset, 'idir')
136
+ return self.__get(fileset, 'idir', is_file=True)
94
137
 
95
138
  ##############################################
96
139
  def add_define(self,
@@ -101,7 +144,8 @@ class DesignSchema(NamedSchema, DependencySchema):
101
144
 
102
145
  Args:
103
146
  value (str or List[str]): Macro definition.
104
- fileset (str, optional): Fileset name.
147
+ fileset (str, optional): Fileset name. If not provided, the active
148
+ fileset is used.
105
149
  clobber (bool, optional): Clears existing list before adding item.
106
150
 
107
151
  Returns:
@@ -114,7 +158,8 @@ class DesignSchema(NamedSchema, DependencySchema):
114
158
  """Returns defined macros for a fileset.
115
159
 
116
160
  Args:
117
- fileset (str): Fileset name.
161
+ fileset (str): Fileset name. If not provided, the active fileset is
162
+ used.
118
163
 
119
164
  Returns:
120
165
  list[str]: List of macro definitions
@@ -130,8 +175,9 @@ class DesignSchema(NamedSchema, DependencySchema):
130
175
 
131
176
  Args:
132
177
  value (str or List[str]): Macro (un)definition.
133
- fileset (str, optional): Fileset name.
134
- clobber (bool, optional): CClears existing list before adding item.
178
+ fileset (str, optional): Fileset name. If not provided, the active
179
+ fileset is used.
180
+ clobber (bool, optional): Clears existing list before adding item.
135
181
 
136
182
  Returns:
137
183
  list[str]: List of macro (un)definitions
@@ -142,7 +188,8 @@ class DesignSchema(NamedSchema, DependencySchema):
142
188
  """Returns undefined macros for a fileset.
143
189
 
144
190
  Args:
145
- fileset (str): Fileset name.
191
+ fileset (str): Fileset name. If not provided, the active fileset is
192
+ used.
146
193
 
147
194
  Returns:
148
195
  list[str]: List of macro (un)definitions
@@ -155,31 +202,33 @@ class DesignSchema(NamedSchema, DependencySchema):
155
202
  value: str,
156
203
  fileset: str = None,
157
204
  clobber: bool = False,
158
- package: str = None) -> List[str]:
205
+ dataroot: str = None) -> List[str]:
159
206
  """Adds dynamic library directories to a fileset.
160
207
 
161
208
  Args:
162
- value (str or List[str]): Library directories
163
- fileset (str, optional): Fileset name.
209
+ value (str or List[str]): Library directories.
210
+ fileset (str, optional): Fileset name. If not provided, the active
211
+ fileset is used.
164
212
  clobber (bool, optional): Clears existing list before adding item.
165
- package (str, optional): Package name
213
+ dataroot (str, optional): Data directory reference name.
166
214
 
167
215
  Returns:
168
216
  list[str]: List of library directories.
169
217
  """
170
218
  return self.__set_add(fileset, 'libdir', value, clobber, typelist=[str, list],
171
- package=package)
219
+ dataroot=dataroot)
172
220
 
173
221
  def get_libdir(self, fileset: str = None) -> List[str]:
174
222
  """Returns dynamic library directories for a fileset.
175
223
 
176
224
  Args:
177
- fileset (str): Fileset name.
225
+ fileset (str): Fileset name. If not provided, the active fileset is
226
+ used.
178
227
 
179
228
  Returns:
180
229
  list[str]: List of library directories.
181
230
  """
182
- return self.__get(fileset, 'libdir')
231
+ return self.__get(fileset, 'libdir', is_file=True)
183
232
 
184
233
  ###############################################
185
234
  def add_lib(self,
@@ -189,8 +238,9 @@ class DesignSchema(NamedSchema, DependencySchema):
189
238
  """Adds dynamic libraries to a fileset.
190
239
 
191
240
  Args:
192
- value (str or List[str]): Libraries
193
- fileset (str, optional): Fileset name.
241
+ value (str or List[str]): Libraries.
242
+ fileset (str, optional): Fileset name. If not provided, the active
243
+ fileset is used.
194
244
  clobber (bool, optional): Clears existing list before adding item.
195
245
 
196
246
  Returns:
@@ -202,7 +252,8 @@ class DesignSchema(NamedSchema, DependencySchema):
202
252
  """Returns list of dynamic libraries for a fileset.
203
253
 
204
254
  Args:
205
- fileset (str): Fileset name.
255
+ fileset (str): Fileset name. If not provided, the active fileset is
256
+ used.
206
257
 
207
258
  Returns:
208
259
  list[str]: List of libraries.
@@ -219,14 +270,15 @@ class DesignSchema(NamedSchema, DependencySchema):
219
270
  Args:
220
271
  name (str): Parameter name.
221
272
  value (str): Parameter value.
222
- fileset (str, optional): Fileset name.
273
+ fileset (str, optional): Fileset name. If not provided, the active
274
+ fileset is used.
223
275
 
224
276
  Returns:
225
277
  str: Parameter value
226
278
  """
227
279
 
228
280
  if fileset is None:
229
- fileset = self.__fileset
281
+ fileset = self._get_active("fileset")
230
282
 
231
283
  if not isinstance(fileset, str):
232
284
  raise ValueError("fileset key must be a string")
@@ -243,144 +295,89 @@ class DesignSchema(NamedSchema, DependencySchema):
243
295
 
244
296
  Args:
245
297
  name (str): Parameter name.
246
- fileset (str): Fileset name.
298
+ fileset (str): Fileset name. If not provided, the active fileset is
299
+ used.
247
300
 
248
301
  Returns:
249
302
  str: Parameter value
250
303
  """
251
304
  if fileset is None:
252
- fileset = self.__fileset
305
+ fileset = self._get_active("fileset")
253
306
 
254
307
  if not isinstance(fileset, str):
255
- raise ValueError("fileset value must be a string")
308
+ raise ValueError("fileset key must be a string")
256
309
  return self.get('fileset', fileset, 'param', name)
257
310
 
258
311
  ###############################################
259
- def add_file(self,
260
- filename: str,
261
- fileset: str = None,
262
- filetype: str = None,
263
- clobber: bool = False,
264
- package: str = None) -> List[str]:
312
+ def add_depfileset(self, dep: Union["DesignSchema", str], depfileset: str, fileset: str = None):
265
313
  """
266
- Adds files to a fileset.
267
-
268
- .v → (source, verilog)
269
- .vhd → (source, vhdl)
270
- .sdc → (constraint, sdc)
271
- .lef → (input, lef)
272
- .def → (input, def)
273
- ... → etc.
314
+ Record a reference to an imported dependency's fileset.
274
315
 
275
316
  Args:
276
- filename (Path or list[Path]): File path or list of paths to add.
277
- fileset (str): Logical group to associate the file with.
278
- filetype (str, optional): Type of the file (e.g., 'verilog', 'sdc').
279
- clobber (bool, optional): Clears list before adding item
280
- package (str, optional): Package name
281
-
282
- Raises:
283
- SiliconCompilerError: If fileset or filetype cannot be inferred from
284
- the file extension.
285
-
286
- Returns:
287
- list[str]: List of file paths.
288
-
289
- Notes:
290
- - This method normalizes `filename` to a string for consistency.
317
+ dep (:class:`DesignSchema` or str): Dependency name or object.
318
+ depfileset (str): Dependency fileset.
319
+ fileset (str): Fileset name. If not provided, the active fileset is
320
+ used.
291
321
 
292
- - If no filetype is specified, filetype is inferred based on
293
- the file extension via a mapping table. (eg. .v is verilog).
294
322
  """
295
-
296
323
  if fileset is None:
297
- fileset = self.__fileset
298
-
299
- # handle list inputs
300
- if isinstance(filename, (list, tuple)):
301
- for item in filename:
302
- self.add_file(
303
- item,
304
- fileset=fileset,
305
- clobber=clobber,
306
- filetype=filetype)
307
- return
308
-
309
- if filename is None:
310
- raise ValueError("add_file cannot process None")
311
-
312
- # Normalize value to string in case we receive a pathlib.Path
313
- filename = str(filename)
314
-
315
- # map extension to default filetype/fileset
316
-
317
- if not filetype:
318
- ext = utils.get_file_ext(filename)
319
- iomap = utils.get_default_iomap()
320
- if ext in iomap:
321
- default_fileset, default_filetype = iomap[ext]
322
- filetype = default_filetype
323
- else:
324
- raise ValueError("illegal file extension")
324
+ fileset = self._get_active("fileset")
325
325
 
326
- # final error checking
327
- if not fileset or not filetype:
328
- raise ValueError(
329
- f'Unable to infer fileset and/or filetype for '
330
- f'{filename} based on file extension.')
326
+ if not isinstance(fileset, str):
327
+ raise ValueError("fileset key must be a string")
331
328
 
332
- # adding files to dictionary
333
- if clobber:
334
- params = self.set('fileset', fileset, 'file', filetype, filename)
329
+ if isinstance(dep, str):
330
+ dep_name = dep
331
+ dep = self.get_dep(dep_name)
332
+ elif isinstance(dep, DesignSchema):
333
+ dep_name = dep.name
334
+ self.add_dep(dep, clobber=True)
335
335
  else:
336
- params = self.add('fileset', fileset, 'file', filetype, filename)
336
+ raise TypeError("dep is not a valid type")
337
337
 
338
- if package and params:
339
- if not isinstance(params, (list, set, tuple)):
340
- params = [params]
338
+ if not isinstance(dep, DesignSchema):
339
+ raise ValueError(f"cannot associate fileset ({depfileset}) with {dep.name}")
341
340
 
342
- for param in params:
343
- param.set(package, field="package")
341
+ if not dep.has_fileset(depfileset):
342
+ raise ValueError(f"{dep.name} does not have {depfileset} as a fileset")
344
343
 
345
- return params
344
+ return self.add("fileset", fileset, "depfileset", (dep_name, depfileset))
346
345
 
347
- ###############################################
348
- def get_file(self,
349
- fileset: str = None,
350
- filetype: str = None):
351
- """Returns a list of files from one or more filesets.
346
+ def get_depfileset(self, fileset: str = None):
347
+ """
348
+ Returns list of dependency filesets.
352
349
 
353
350
  Args:
354
- fileset (str or list[str]): Fileset(s) to query.
355
- filetype (str or list[str], optional): File type(s) to filter by (e.g., 'verilog').
351
+ fileset (str): Fileset name. If not provided, the active fileset is
352
+ used.
356
353
 
357
354
  Returns:
358
- list[str]: List of file paths.
355
+ list[tuple(str, str)]: List of dependencies and filesets.
359
356
  """
360
-
361
357
  if fileset is None:
362
- fileset = self.__fileset
358
+ fileset = self._get_active("fileset")
363
359
 
364
- if not isinstance(fileset, list):
365
- fileset = [fileset]
360
+ if not isinstance(fileset, str):
361
+ raise ValueError("fileset key must be a string")
366
362
 
367
- if filetype and not isinstance(filetype, list):
368
- filetype = [filetype]
363
+ return self.get("fileset", fileset, "depfileset")
369
364
 
370
- filelist = []
371
- for i in fileset:
372
- if not isinstance(i, str):
373
- raise ValueError("fileset key must be a string")
374
- # handle scalar+list in argument
375
- if not filetype:
376
- filetype = list(self.getkeys('fileset', i, 'file'))
377
- # grab the files
378
- for j in filetype:
379
- filelist.extend(self.get('fileset', i, 'file', j))
365
+ def __write_flist(self,
366
+ filename: str,
367
+ filesets: List[str],
368
+ depalias: Dict[str, Tuple[NamedSchema, str]]):
369
+ '''
370
+ Internal helper to write a Verilog-style file list (`.f` file).
380
371
 
381
- return filelist
372
+ This method iterates through the specified filesets (and their
373
+ dependencies), writing out `+incdir+`, `+define+`, and source file
374
+ paths.
382
375
 
383
- def __write_flist(self, filename: str, filesets: list):
376
+ Args:
377
+ filename (str): The path to the output file list.
378
+ filesets (List[str]): A list of fileset names to include.
379
+ depalias (Dict): A dictionary for aliasing dependencies.
380
+ '''
384
381
  written_cmd = set()
385
382
 
386
383
  with open(filename, "w") as f:
@@ -394,33 +391,50 @@ class DesignSchema(NamedSchema, DependencySchema):
394
391
  def write_header(header):
395
392
  f.write(f"// {header}\n")
396
393
 
397
- for lib in [self, *self.get_dep()]:
398
- write_header(f"{lib.name()}")
399
- for fileset in filesets:
400
- if not lib.valid('fileset', fileset):
401
- continue
394
+ for lib, fileset in self.get_fileset(filesets, depalias):
395
+ if lib.get('fileset', fileset, 'idir'):
396
+ write_header(f"{lib.name} / {fileset} / include directories")
397
+ for idir in lib.find_files('fileset', fileset, 'idir'):
398
+ write(f"+incdir+{idir}")
402
399
 
403
- if lib.get('fileset', fileset, 'idir'):
404
- write_header(f"{lib.name()} / {fileset} / include directories")
405
- for idir in lib.find_files('fileset', fileset, 'idir'):
406
- write(f"+incdir+{idir}")
400
+ if lib.get('fileset', fileset, 'define'):
401
+ write_header(f"{lib.name} / {fileset} / defines")
402
+ for define in lib.get('fileset', fileset, 'define'):
403
+ write(f"+define+{define}")
407
404
 
408
- if lib.get('fileset', fileset, 'define'):
409
- write_header(f"{lib.name()} / {fileset} / defines")
410
- for define in lib.get('fileset', fileset, 'define'):
411
- write(f"+define+{define}")
405
+ for filetype in lib.getkeys('fileset', fileset, 'file'):
406
+ if lib.get('fileset', fileset, 'file', filetype):
407
+ write_header(f"{lib.name} / {fileset} / {filetype} files")
408
+ for file in lib.find_files('fileset', fileset, 'file', filetype):
409
+ write(file)
412
410
 
413
- for filetype in lib.getkeys('fileset', fileset, 'file'):
414
- if lib.get('fileset', fileset, 'file', filetype):
415
- write_header(f"{lib.name()} / {fileset} / {filetype} files")
416
- for file in lib.find_files('fileset', fileset, 'file', filetype):
417
- write(file)
411
+ def __map_fileformat(self, path):
412
+ '''
413
+ Internal helper to determine file format from a file extension.
414
+
415
+ Args:
416
+ path (str): The file path.
417
+
418
+ Returns:
419
+ str: The determined file format (e.g., "flist").
420
+
421
+ Raises:
422
+ ValueError: If the file format cannot be determined from the
423
+ extension.
424
+ '''
425
+ _, ext = os.path.splitext(path)
426
+
427
+ if ext == ".f":
428
+ return "flist"
429
+ else:
430
+ raise ValueError(f"Unable to determine filetype of: {path}")
418
431
 
419
432
  ###############################################
420
433
  def write_fileset(self,
421
434
  filename: str,
422
435
  fileset: str = None,
423
- fileformat: str = None) -> None:
436
+ fileformat: str = None,
437
+ depalias: Dict[str, Tuple[NamedSchema, str]] = None) -> None:
424
438
  """Exports filesets to a standard formatted text file.
425
439
 
426
440
  Currently supports Verilog `flist` format only.
@@ -429,15 +443,17 @@ class DesignSchema(NamedSchema, DependencySchema):
429
443
 
430
444
  Args:
431
445
  filename (str or Path): Output file name.
432
- fileset (str or list[str]): Fileset(s) to export.
446
+ fileset (str or list[str]): Fileset(s) to export. If not provided,
447
+ the active fileset is used.
433
448
  fileformat (str, optional): Export format.
449
+ depalias (dict of schema objects): Map of aliased objects.
434
450
  """
435
451
 
436
452
  if filename is None:
437
- raise ValueError("write_fileset() filename cannot be None")
453
+ raise ValueError("filename cannot be None")
438
454
 
439
455
  if fileset is None:
440
- fileset = self.__fileset
456
+ fileset = self._get_active("fileset")
441
457
 
442
458
  if not isinstance(fileset, list):
443
459
  fileset = [fileset]
@@ -448,16 +464,24 @@ class DesignSchema(NamedSchema, DependencySchema):
448
464
 
449
465
  # file extension lookup
450
466
  if not fileformat:
451
- formats = {}
452
- formats['f'] = 'flist'
453
- fileformat = formats[Path(filename).suffix.strip('.')]
467
+ fileformat = self.__map_fileformat(filename)
454
468
 
455
469
  if fileformat == "flist":
456
- self.__write_flist(filename, fileset)
470
+ self.__write_flist(filename, fileset, depalias)
457
471
  else:
458
- raise ValueError(f"{fileformat} is not supported")
472
+ raise ValueError(f"{fileformat} is not a supported filetype")
459
473
 
460
474
  def __read_flist(self, filename: str, fileset: str):
475
+ '''
476
+ Internal helper to read a Verilog-style file list (`.f` file).
477
+
478
+ This method parses the file list for `+incdir+`, `+define+`, and
479
+ source files, and populates the specified fileset in the schema.
480
+
481
+ Args:
482
+ filename (str): The path to the input file list.
483
+ fileset (str): The name of the fileset to populate.
484
+ '''
461
485
  # Extract information
462
486
  rel_path = os.path.dirname(os.path.abspath(filename))
463
487
 
@@ -485,26 +509,26 @@ class DesignSchema(NamedSchema, DependencySchema):
485
509
  else:
486
510
  files.append(expand_path(line))
487
511
 
488
- # Create packages
512
+ # Create dataroots
489
513
  all_paths = include_dirs + [os.path.dirname(f) for f in files]
490
514
  all_paths = sorted(set(all_paths))
491
515
 
492
- package_root_name = f'flist-{self.name()}-{fileset}-{os.path.basename(filename)}'
493
- packages = {}
516
+ dataroot_root_name = f'flist-{self.name}-{fileset}-{os.path.basename(filename)}'
517
+ dataroots = {}
494
518
 
495
519
  for path_dir in all_paths:
496
520
  found = False
497
- for pdir in packages:
521
+ for pdir in dataroots:
498
522
  if path_dir.startswith(pdir):
499
523
  found = True
500
524
  break
501
525
  if not found:
502
- package_name = f"{package_root_name}-{len(packages)}"
503
- self.register_package(package_name, path_dir)
504
- packages[path_dir] = package_name
526
+ dataroot_name = f"{dataroot_root_name}-{len(dataroots)}"
527
+ self.set_dataroot(dataroot_name, path_dir)
528
+ dataroots[path_dir] = dataroot_name
505
529
 
506
- def get_package(path):
507
- for pdir, name in packages.items():
530
+ def get_dataroot(path):
531
+ for pdir, name in dataroots.items():
508
532
  if path.startswith(pdir):
509
533
  return name, pdir
510
534
  return None, None
@@ -515,21 +539,21 @@ class DesignSchema(NamedSchema, DependencySchema):
515
539
  self.add_define(defines)
516
540
  if include_dirs:
517
541
  for dir in include_dirs:
518
- package_name, pdir = get_package(dir)
519
- if package_name:
542
+ dataroot_name, pdir = get_dataroot(dir)
543
+ if dataroot_name:
520
544
  dir = os.path.relpath(dir, pdir)
521
- self.add_idir(dir, package=package_name)
545
+ self.add_idir(dir, dataroot=dataroot_name)
522
546
  if files:
523
547
  for f in files:
524
- package_name, pdir = get_package(f)
525
- if package_name:
548
+ dataroot_name, pdir = get_dataroot(f)
549
+ if dataroot_name:
526
550
  f = os.path.relpath(f, pdir)
527
- self.add_file(f, package=package_name)
551
+ self.add_file(f, dataroot=dataroot_name)
528
552
 
529
553
  ################################################
530
554
  def read_fileset(self,
531
555
  filename: str,
532
- fileset: str,
556
+ fileset: str = None,
533
557
  fileformat=None) -> None:
534
558
  """Imports filesets from a standard formatted text file.
535
559
 
@@ -537,33 +561,49 @@ class DesignSchema(NamedSchema, DependencySchema):
537
561
  Intended to support other formats in the future.
538
562
 
539
563
  Args:
540
- filename (str or Path): Output file name.
541
- fileset (str or list[str]): Filesets to import.
542
- fileformat (str, optional): Export format.
564
+ filename (str or Path): Input file name.
565
+ fileset (str or list[str]): Fileset to import into. If not
566
+ provided, the active fileset is used.
567
+ fileformat (str, optional): Import format. Inferred from file
568
+ extension if not provided.
543
569
  """
544
570
 
545
571
  if filename is None:
546
- raise ValueError("read_fileset() filename cannot be None")
572
+ raise ValueError("filename cannot be None")
547
573
 
548
574
  if not fileformat:
549
- formats = {}
550
- formats['f'] = 'flist'
551
- fileformat = formats[Path(filename).suffix.strip('.')]
575
+ fileformat = self.__map_fileformat(filename)
576
+
577
+ if fileset is None:
578
+ fileset = self._get_active("fileset")
552
579
 
553
580
  if fileformat == "flist":
554
581
  self.__read_flist(filename, fileset)
555
582
  else:
556
- raise ValueError(f"{fileformat} is not supported")
583
+ raise ValueError(f"{fileformat} is not a supported filetype")
557
584
 
558
585
  ################################################
559
586
  # Helper Functions
560
587
  ################################################
561
- def __set_add(self, fileset, option, value, clobber=False, typelist=None, package=None):
562
- '''Sets a parameter value in schema.
588
+ def __set_add(self, fileset, option, value, clobber=False, typelist=None, dataroot=None):
589
+ '''
590
+ Internal helper to set or add a parameter value in the schema.
591
+
592
+ This function handles common tasks for setters like `add_idir` and
593
+ `add_define`, such as resolving the active fileset, checking value
594
+ types, and calling the underlying schema `set()` or `add()` methods.
595
+
596
+ Args:
597
+ fileset (str): The fileset to modify.
598
+ option (str): The parameter key to modify.
599
+ value: The value to set or add.
600
+ clobber (bool): If True, overwrite the existing value.
601
+ typelist (list): A list of allowed types for the value.
602
+ dataroot (str): The dataroot to associate with the value.
563
603
  '''
564
604
 
565
605
  if fileset is None:
566
- fileset = self.__fileset
606
+ fileset = self._get_active("fileset")
567
607
 
568
608
  # check for a legal fileset
569
609
  if not fileset or not isinstance(fileset, str):
@@ -581,84 +621,121 @@ class DesignSchema(NamedSchema, DependencySchema):
581
621
  if value is None:
582
622
  raise ValueError(f"None is an illegal {option} value")
583
623
 
584
- if list in typelist and not clobber:
585
- params = self.add('fileset', fileset, option, value)
586
- else:
587
- params = self.set('fileset', fileset, option, value)
588
-
589
- if package and params:
590
- if not isinstance(params, (list, set, tuple)):
591
- params = [params]
624
+ if not dataroot:
625
+ dataroot = self._get_active("package")
592
626
 
593
- for param in params:
594
- param.set(package, field="package")
627
+ with self.active_dataroot(dataroot):
628
+ if list in typelist and not clobber:
629
+ params = self.add('fileset', fileset, option, value)
630
+ else:
631
+ params = self.set('fileset', fileset, option, value)
595
632
 
596
633
  return params
597
634
 
598
- def __get(self, fileset, option):
599
- '''Gets a parameter value from schema.
635
+ def __get(self, fileset, option, is_file=False):
636
+ '''
637
+ Internal helper to get a parameter value from the schema.
638
+
639
+ This function handles common tasks for getters, such as resolving the
640
+ active fileset and optionally resolving file paths.
641
+
642
+ Args:
643
+ fileset (str): The fileset to query.
644
+ option (str): The parameter key to retrieve.
645
+ is_file (bool): If True, treat the value as a file path and
646
+ resolve it using `find_files`.
600
647
  '''
601
648
  if fileset is None:
602
- fileset = self.__fileset
649
+ fileset = self._get_active("fileset")
603
650
 
604
651
  if not isinstance(fileset, str):
605
652
  raise ValueError("fileset key must be a string")
653
+ if is_file:
654
+ return self.find_files('fileset', fileset, option)
606
655
  return self.get('fileset', fileset, option)
607
656
 
608
- @contextlib.contextmanager
609
- def active_fileset(self, fileset: str):
657
+ @classmethod
658
+ def _getdict_type(cls) -> str:
610
659
  """
611
- Use this context to temporarily set a design fileset.
660
+ Returns the meta data for getdict.
612
661
 
613
- Raises:
614
- TypeError: if fileset is not a string
615
- ValueError: if fileset if an empty string
662
+ This is used to identify the object type during serialization.
663
+ """
664
+
665
+ return DesignSchema.__name__
666
+
667
+ def get_fileset(self,
668
+ filesets: Union[List[str], str],
669
+ alias: Dict[str, Tuple[NamedSchema, str]] = None) -> \
670
+ List[Tuple[NamedSchema, str]]:
671
+ """
672
+ Computes the full, recursive list of (dependency, fileset) tuples
673
+ required for a given set of top-level filesets.
674
+
675
+ This method traverses the design's dependency graph.
616
676
 
617
677
  Args:
618
- fileset (str): name of the fileset
678
+ filesets (list of str): List of top-level filesets to evaluate.
679
+ alias (dict of schema objects): Map of aliased objects to
680
+ substitute during traversal.
619
681
 
620
- Example:
621
- >>> with design.active_fileset("rtl"):
622
- ... design.set_topmodule("top")
623
- Sets the top module for the rtl fileset as top.
682
+ Returns:
683
+ List[Tuple[NamedSchema, str]]: A flattened, unique list of
684
+ (dependency, fileset) tuples.
624
685
  """
625
- if not isinstance(fileset, str):
626
- raise TypeError("fileset must a string")
627
- if not fileset:
628
- raise ValueError("fileset cannot be an empty string")
686
+ if alias is None:
687
+ alias = {}
688
+
689
+ if isinstance(filesets, str):
690
+ # Ensure we have a list
691
+ filesets = [filesets]
692
+
693
+ mapping = []
694
+ for fileset in filesets:
695
+ self._assert_fileset(fileset)
696
+
697
+ mapping.append((self, fileset))
698
+ for dep, depfileset in self.get("fileset", fileset, "depfileset"):
699
+ if (dep, depfileset) in alias:
700
+ dep_obj, new_depfileset = alias[(dep, depfileset)]
701
+ if dep_obj is None:
702
+ continue
703
+
704
+ if new_depfileset:
705
+ depfileset = new_depfileset
706
+ else:
707
+ dep_obj = self.get_dep(dep)
708
+ if not isinstance(dep_obj, DesignSchema):
709
+ raise TypeError(f"{dep} must be a design object.")
629
710
 
630
- self.__fileset = fileset
631
- yield
632
- self.__fileset = None
711
+ mapping.extend(dep_obj.get_fileset(depfileset, alias))
712
+
713
+ # Cleanup
714
+ final_map = []
715
+ for cmap in mapping:
716
+ if cmap not in final_map:
717
+ final_map.append(cmap)
718
+ return final_map
633
719
 
634
720
 
635
721
  ###########################################################################
636
722
  # Schema
637
723
  ###########################################################################
638
724
  def schema_design(schema):
725
+ '''
726
+ Defines the schema parameters specific to a design.
639
727
 
640
- schema = EditableSchema(schema)
728
+ This function is called by the `DesignSchema` constructor to set up
729
+ its unique schema elements, such as `topmodule`, `idir`, `define`, etc.,
730
+ under the `fileset` key.
641
731
 
642
- ###########################
643
- # Files
644
- ###########################
732
+ Args:
733
+ schema (DesignSchema): The schema object to configure.
734
+ '''
735
+
736
+ schema = EditableSchema(schema)
645
737
 
646
738
  fileset = 'default'
647
- filetype = 'default'
648
- schema.insert(
649
- 'fileset', fileset, 'file', filetype,
650
- Parameter(
651
- ['file'],
652
- scope=Scope.GLOBAL,
653
- shorthelp="Design files",
654
- example=[
655
- "api: chip.set('fileset', 'rtl', 'file', 'verilog', 'mytop.v')",
656
- "api: chip.set('fileset', 'testbench', 'file', 'verilog', 'tb.v')"],
657
- help=trim("""
658
- List of files grouped as a named set ('fileset'). The exact names of
659
- filetypes and filesets must match the names used in tasks
660
- called during flowgraph execution. The files are processed in
661
- the order specified by the ordered file list.""")))
662
739
 
663
740
  ###########################
664
741
  # Options
@@ -726,8 +803,7 @@ def schema_design(schema):
726
803
  help=trim("""
727
804
  Specifies directories to scan for libraries provided with the
728
805
  :keypath:`lib` parameter. If multiple paths are provided, they are
729
- searched based on the order of the libdir list. The libdir
730
- parameter is translated to the '-y' option in verilog based tools.""")))
806
+ searched based on the order of the libdir list.""")))
731
807
 
732
808
  schema.insert(
733
809
  'fileset', fileset, 'lib',
@@ -756,3 +832,13 @@ def schema_design(schema):
756
832
  data literals. The types of parameters and values supported is tightly
757
833
  coupled to tools being used. For example, in Verilog only integer
758
834
  literals (64'h4, 2'b0, 4) and strings are supported.""")))
835
+
836
+ schema.insert(
837
+ 'fileset', fileset, 'depfileset',
838
+ Parameter(
839
+ '[(str,str)]',
840
+ scope=Scope.GLOBAL,
841
+ shorthelp="Design dependency fileset",
842
+ example=[
843
+ "api: chip.set('fileset', 'rtl, 'depfileset', ('lambdalib', 'rtl')"],
844
+ help=trim("""Sets the mapping for dependency filesets.""")))