siliconcompiler 0.34.0__py3-none-any.whl → 0.34.2__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 (114) hide show
  1. siliconcompiler/__init__.py +14 -2
  2. siliconcompiler/_metadata.py +1 -1
  3. siliconcompiler/apps/_common.py +1 -1
  4. siliconcompiler/apps/sc.py +1 -1
  5. siliconcompiler/apps/sc_issue.py +1 -1
  6. siliconcompiler/apps/sc_remote.py +3 -3
  7. siliconcompiler/apps/sc_show.py +3 -3
  8. siliconcompiler/apps/utils/replay.py +4 -4
  9. siliconcompiler/checklist.py +203 -2
  10. siliconcompiler/constraints/__init__.py +17 -0
  11. siliconcompiler/constraints/asic_component.py +378 -0
  12. siliconcompiler/constraints/asic_floorplan.py +449 -0
  13. siliconcompiler/constraints/asic_pins.py +489 -0
  14. siliconcompiler/constraints/asic_timing.py +517 -0
  15. siliconcompiler/core.py +31 -249
  16. siliconcompiler/data/templates/email/general.j2 +3 -3
  17. siliconcompiler/data/templates/email/summary.j2 +1 -1
  18. siliconcompiler/data/templates/issue/README.txt +1 -1
  19. siliconcompiler/data/templates/report/sc_report.j2 +7 -7
  20. siliconcompiler/dependencyschema.py +10 -174
  21. siliconcompiler/design.py +325 -114
  22. siliconcompiler/flowgraph.py +63 -15
  23. siliconcompiler/library.py +133 -0
  24. siliconcompiler/metric.py +94 -72
  25. siliconcompiler/metrics/__init__.py +7 -0
  26. siliconcompiler/metrics/asic.py +245 -0
  27. siliconcompiler/metrics/fpga.py +220 -0
  28. siliconcompiler/optimizer/vizier.py +2 -2
  29. siliconcompiler/package/__init__.py +138 -35
  30. siliconcompiler/package/github.py +6 -10
  31. siliconcompiler/packageschema.py +256 -12
  32. siliconcompiler/pathschema.py +226 -0
  33. siliconcompiler/pdk.py +5 -5
  34. siliconcompiler/project.py +459 -0
  35. siliconcompiler/remote/client.py +18 -12
  36. siliconcompiler/remote/server.py +2 -2
  37. siliconcompiler/report/dashboard/cli/__init__.py +6 -6
  38. siliconcompiler/report/dashboard/cli/board.py +3 -3
  39. siliconcompiler/report/dashboard/web/components/__init__.py +5 -5
  40. siliconcompiler/report/dashboard/web/components/flowgraph.py +4 -4
  41. siliconcompiler/report/dashboard/web/components/graph.py +2 -2
  42. siliconcompiler/report/dashboard/web/state.py +1 -1
  43. siliconcompiler/report/dashboard/web/utils/__init__.py +5 -5
  44. siliconcompiler/report/html_report.py +1 -1
  45. siliconcompiler/report/report.py +4 -4
  46. siliconcompiler/report/summary_table.py +2 -2
  47. siliconcompiler/report/utils.py +5 -5
  48. siliconcompiler/scheduler/docker.py +4 -10
  49. siliconcompiler/scheduler/run_node.py +4 -8
  50. siliconcompiler/scheduler/scheduler.py +18 -24
  51. siliconcompiler/scheduler/schedulernode.py +161 -143
  52. siliconcompiler/scheduler/send_messages.py +3 -3
  53. siliconcompiler/scheduler/slurm.py +5 -3
  54. siliconcompiler/scheduler/taskscheduler.py +10 -8
  55. siliconcompiler/schema/__init__.py +0 -2
  56. siliconcompiler/schema/baseschema.py +148 -26
  57. siliconcompiler/schema/editableschema.py +14 -6
  58. siliconcompiler/schema/journal.py +23 -15
  59. siliconcompiler/schema/namedschema.py +30 -4
  60. siliconcompiler/schema/parameter.py +34 -19
  61. siliconcompiler/schema/parametertype.py +2 -0
  62. siliconcompiler/schema/parametervalue.py +198 -15
  63. siliconcompiler/schema/schema_cfg.py +18 -14
  64. siliconcompiler/schema_obj.py +5 -3
  65. siliconcompiler/tool.py +591 -179
  66. siliconcompiler/tools/__init__.py +2 -0
  67. siliconcompiler/tools/builtin/_common.py +5 -5
  68. siliconcompiler/tools/builtin/concatenate.py +5 -5
  69. siliconcompiler/tools/builtin/minimum.py +4 -4
  70. siliconcompiler/tools/builtin/mux.py +4 -4
  71. siliconcompiler/tools/builtin/nop.py +4 -4
  72. siliconcompiler/tools/builtin/verify.py +7 -7
  73. siliconcompiler/tools/execute/exec_input.py +1 -1
  74. siliconcompiler/tools/genfasm/genfasm.py +1 -6
  75. siliconcompiler/tools/openroad/_apr.py +5 -1
  76. siliconcompiler/tools/openroad/antenna_repair.py +1 -1
  77. siliconcompiler/tools/openroad/macro_placement.py +1 -1
  78. siliconcompiler/tools/openroad/power_grid.py +1 -1
  79. siliconcompiler/tools/openroad/scripts/common/procs.tcl +5 -0
  80. siliconcompiler/tools/opensta/timing.py +26 -3
  81. siliconcompiler/tools/slang/__init__.py +2 -2
  82. siliconcompiler/tools/surfer/__init__.py +0 -0
  83. siliconcompiler/tools/surfer/show.py +53 -0
  84. siliconcompiler/tools/surfer/surfer.py +30 -0
  85. siliconcompiler/tools/vpr/route.py +27 -14
  86. siliconcompiler/tools/vpr/vpr.py +23 -6
  87. siliconcompiler/tools/yosys/__init__.py +1 -1
  88. siliconcompiler/tools/yosys/scripts/procs.tcl +143 -0
  89. siliconcompiler/tools/yosys/{sc_synth_asic.tcl → scripts/sc_synth_asic.tcl} +4 -0
  90. siliconcompiler/tools/yosys/{sc_synth_fpga.tcl → scripts/sc_synth_fpga.tcl} +24 -77
  91. siliconcompiler/tools/yosys/syn_fpga.py +14 -0
  92. siliconcompiler/toolscripts/_tools.json +9 -13
  93. siliconcompiler/toolscripts/rhel9/install-vpr.sh +0 -2
  94. siliconcompiler/toolscripts/ubuntu22/install-surfer.sh +33 -0
  95. siliconcompiler/toolscripts/ubuntu24/install-surfer.sh +33 -0
  96. siliconcompiler/utils/__init__.py +2 -1
  97. siliconcompiler/utils/flowgraph.py +24 -23
  98. siliconcompiler/utils/issue.py +23 -29
  99. siliconcompiler/utils/logging.py +35 -6
  100. siliconcompiler/utils/showtools.py +6 -1
  101. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/METADATA +15 -25
  102. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/RECORD +109 -97
  103. siliconcompiler/schema/packageschema.py +0 -101
  104. siliconcompiler/tools/yosys/procs.tcl +0 -71
  105. siliconcompiler/toolscripts/rhel9/install-yosys-parmys.sh +0 -68
  106. siliconcompiler/toolscripts/ubuntu22/install-yosys-parmys.sh +0 -68
  107. siliconcompiler/toolscripts/ubuntu24/install-yosys-parmys.sh +0 -68
  108. /siliconcompiler/tools/yosys/{sc_lec.tcl → scripts/sc_lec.tcl} +0 -0
  109. /siliconcompiler/tools/yosys/{sc_screenshot.tcl → scripts/sc_screenshot.tcl} +0 -0
  110. /siliconcompiler/tools/yosys/{syn_strategies.tcl → scripts/syn_strategies.tcl} +0 -0
  111. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/WHEEL +0 -0
  112. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/entry_points.txt +0 -0
  113. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/licenses/LICENSE +0 -0
  114. {siliconcompiler-0.34.0.dist-info → siliconcompiler-0.34.2.dist-info}/top_level.txt +0 -0
siliconcompiler/design.py CHANGED
@@ -1,28 +1,38 @@
1
1
  import contextlib
2
2
  import re
3
3
 
4
- from pathlib import Path
5
- from typing import List
4
+ import os.path
5
+
6
+ from typing import List, Union, Tuple, Dict
6
7
 
7
8
  from siliconcompiler import utils
8
- from siliconcompiler import SiliconCompilerError
9
+
10
+ from siliconcompiler import PackageSchema
9
11
 
10
12
  from siliconcompiler.dependencyschema import DependencySchema
13
+ from siliconcompiler.pathschema import PathSchema
11
14
  from siliconcompiler.schema import NamedSchema
12
15
  from siliconcompiler.schema import EditableSchema, Parameter, Scope
13
16
  from siliconcompiler.schema.utils import trim
14
17
 
15
18
 
16
19
  ###########################################################################
17
- class DesignSchema(NamedSchema, DependencySchema):
20
+ class DesignSchema(PackageSchema, NamedSchema, DependencySchema, PathSchema):
18
21
 
19
- def __init__(self, name: str):
20
- NamedSchema.__init__(self, name=name)
21
- DependencySchema.__init__(self)
22
+ def __init__(self, name: str = None):
23
+ super().__init__()
24
+ self.set_name(name)
22
25
 
23
26
  schema_design(self)
24
27
 
25
- self.__fileset = None
28
+ def add_dep(self, obj: NamedSchema, clobber: bool = True) -> bool:
29
+ if not isinstance(obj, NamedSchema):
30
+ raise TypeError(f"Cannot add an object of type: {type(obj)}")
31
+
32
+ if obj.name() == self.name():
33
+ raise ValueError("Cannot add a dependency with the same name")
34
+
35
+ return super().add_dep(obj, clobber=clobber)
26
36
 
27
37
  ############################################
28
38
  def set_topmodule(self,
@@ -38,9 +48,8 @@ class DesignSchema(NamedSchema, DependencySchema):
38
48
  str: Topmodule name
39
49
 
40
50
  Notes:
41
- - first character must be letter or underscore
42
- - remaining characters can be letters, digits, or underscores
43
-
51
+ - first character must be letter or underscore
52
+ - remaining characters can be letters, digits, or underscores
44
53
  """
45
54
 
46
55
  # topmodule safety check
@@ -58,7 +67,6 @@ class DesignSchema(NamedSchema, DependencySchema):
58
67
 
59
68
  Returns:
60
69
  str: Topmodule name
61
-
62
70
  """
63
71
  return self.__get(fileset, 'topmodule')
64
72
 
@@ -67,20 +75,20 @@ class DesignSchema(NamedSchema, DependencySchema):
67
75
  value: str,
68
76
  fileset: str = None,
69
77
  clobber: bool = False,
70
- package: str = None) -> List[str]:
78
+ dataroot: str = None) -> List[str]:
71
79
  """Adds include directories to a fileset.
72
80
 
73
81
  Args:
74
82
  value (str or Path): Include directory name.
75
83
  fileset (str, optional): Fileset name.
76
84
  clobber (bool, optional): Clears existing list before adding item
77
- package (str, optional): Package name
85
+ dataroot (str, optional): Data directory reference name
78
86
 
79
87
  Returns:
80
88
  list[str]: List of include directories
81
-
82
89
  """
83
- return self.__set_add(fileset, 'idir', value, clobber, typelist=[str, list])
90
+ return self.__set_add(fileset, 'idir', value, clobber, typelist=[str, list],
91
+ dataroot=dataroot)
84
92
 
85
93
  def get_idir(self, fileset: str = None) -> List[str]:
86
94
  """Returns include directories for a fileset.
@@ -90,9 +98,8 @@ class DesignSchema(NamedSchema, DependencySchema):
90
98
 
91
99
  Returns:
92
100
  list[str]: List of include directories
93
-
94
101
  """
95
- return self.__get(fileset, 'idir')
102
+ return self.__get(fileset, 'idir', is_file=True)
96
103
 
97
104
  ##############################################
98
105
  def add_define(self,
@@ -120,7 +127,6 @@ class DesignSchema(NamedSchema, DependencySchema):
120
127
 
121
128
  Returns:
122
129
  list[str]: List of macro definitions
123
-
124
130
  """
125
131
  return self.__get(fileset, 'define')
126
132
 
@@ -138,7 +144,6 @@ class DesignSchema(NamedSchema, DependencySchema):
138
144
 
139
145
  Returns:
140
146
  list[str]: List of macro (un)definitions
141
-
142
147
  """
143
148
  return self.__set_add(fileset, 'undefine', value, clobber, typelist=[str, list])
144
149
 
@@ -159,20 +164,20 @@ class DesignSchema(NamedSchema, DependencySchema):
159
164
  value: str,
160
165
  fileset: str = None,
161
166
  clobber: bool = False,
162
- package: str = None) -> List[str]:
167
+ dataroot: str = None) -> List[str]:
163
168
  """Adds dynamic library directories to a fileset.
164
169
 
165
170
  Args:
166
171
  value (str or List[str]): Library directories
167
172
  fileset (str, optional): Fileset name.
168
173
  clobber (bool, optional): Clears existing list before adding item.
169
- package (str, optional): Package name
174
+ dataroot (str, optional): Data directory reference name
170
175
 
171
176
  Returns:
172
177
  list[str]: List of library directories.
173
-
174
178
  """
175
- return self.__set_add(fileset, 'libdir', value, clobber, typelist=[str, list])
179
+ return self.__set_add(fileset, 'libdir', value, clobber, typelist=[str, list],
180
+ dataroot=dataroot)
176
181
 
177
182
  def get_libdir(self, fileset: str = None) -> List[str]:
178
183
  """Returns dynamic library directories for a fileset.
@@ -182,9 +187,8 @@ class DesignSchema(NamedSchema, DependencySchema):
182
187
 
183
188
  Returns:
184
189
  list[str]: List of library directories.
185
-
186
190
  """
187
- return self.__get(fileset, 'libdir')
191
+ return self.__get(fileset, 'libdir', is_file=True)
188
192
 
189
193
  ###############################################
190
194
  def add_lib(self,
@@ -200,7 +204,6 @@ class DesignSchema(NamedSchema, DependencySchema):
200
204
 
201
205
  Returns:
202
206
  list[str]: List of libraries.
203
-
204
207
  """
205
208
  return self.__set_add(fileset, 'lib', value, clobber, typelist=[str, list])
206
209
 
@@ -212,7 +215,6 @@ class DesignSchema(NamedSchema, DependencySchema):
212
215
 
213
216
  Returns:
214
217
  list[str]: List of libraries.
215
-
216
218
  """
217
219
  return self.__get(fileset, 'lib')
218
220
 
@@ -230,11 +232,10 @@ class DesignSchema(NamedSchema, DependencySchema):
230
232
 
231
233
  Returns:
232
234
  str: Parameter value
233
-
234
235
  """
235
236
 
236
237
  if fileset is None:
237
- fileset = self.__fileset
238
+ fileset = self._get_active("fileset")
238
239
 
239
240
  if not isinstance(fileset, str):
240
241
  raise ValueError("fileset key must be a string")
@@ -257,24 +258,74 @@ class DesignSchema(NamedSchema, DependencySchema):
257
258
  str: Parameter value
258
259
  """
259
260
  if fileset is None:
260
- fileset = self.__fileset
261
+ fileset = self._get_active("fileset")
261
262
 
262
263
  if not isinstance(fileset, str):
263
- raise ValueError("fileset value must be a string")
264
+ raise ValueError("fileset key must be a string")
264
265
  return self.get('fileset', fileset, 'param', name)
265
266
 
267
+ ###############################################
268
+ def add_depfileset(self, dep: Union["DesignSchema", str], depfileset: str, fileset: str = None):
269
+ """
270
+ Record a reference to an imported dependency's fileset.
271
+
272
+ Args:
273
+ dep (:class:`DesignSchema` or str): Dependency name or object.
274
+ depfileset (str): Dependency fileset
275
+ fileset (str): Fileset name.
276
+
277
+ """
278
+ if fileset is None:
279
+ fileset = self._get_active("fileset")
280
+
281
+ if not isinstance(fileset, str):
282
+ raise ValueError("fileset key must be a string")
283
+
284
+ if isinstance(dep, str):
285
+ dep_name = dep
286
+ dep = self.get_dep(dep_name)
287
+ elif isinstance(dep, DesignSchema):
288
+ dep_name = dep.name()
289
+ self.add_dep(dep, clobber=True)
290
+ else:
291
+ raise TypeError("dep is not a valid type")
292
+
293
+ if not isinstance(dep, DesignSchema):
294
+ raise ValueError(f"cannot associate fileset ({depfileset}) with {dep.name()}")
295
+
296
+ if depfileset not in dep.getkeys("fileset"):
297
+ raise ValueError(f"{dep.name()} does not have {depfileset} as a fileset")
298
+
299
+ return self.add("fileset", fileset, "depfileset", (dep_name, depfileset))
300
+
301
+ def get_depfileset(self, fileset: str = None):
302
+ """
303
+ Returns list of dependency filesets.
304
+
305
+ Args:
306
+ fileset (str): Fileset name.
307
+
308
+ Returns:
309
+ list[str]: List of dependencies and filesets.
310
+ """
311
+ if fileset is None:
312
+ fileset = self._get_active("fileset")
313
+
314
+ if not isinstance(fileset, str):
315
+ raise ValueError("fileset key must be a string")
316
+
317
+ return self.get("fileset", fileset, "depfileset")
318
+
266
319
  ###############################################
267
320
  def add_file(self,
268
321
  filename: str,
269
322
  fileset: str = None,
270
323
  filetype: str = None,
271
324
  clobber: bool = False,
272
- package: str = None) -> List[str]:
325
+ dataroot: str = None) -> List[str]:
273
326
  """
274
327
  Adds files to a fileset.
275
328
 
276
-
277
-
278
329
  .v → (source, verilog)
279
330
  .vhd → (source, vhdl)
280
331
  .sdc → (constraint, sdc)
@@ -287,7 +338,7 @@ class DesignSchema(NamedSchema, DependencySchema):
287
338
  fileset (str): Logical group to associate the file with.
288
339
  filetype (str, optional): Type of the file (e.g., 'verilog', 'sdc').
289
340
  clobber (bool, optional): Clears list before adding item
290
- package (str, optional): Package name
341
+ dataroot (str, optional): Data directory reference name
291
342
 
292
343
  Raises:
293
344
  SiliconCompilerError: If fileset or filetype cannot be inferred from
@@ -300,23 +351,26 @@ class DesignSchema(NamedSchema, DependencySchema):
300
351
  - This method normalizes `filename` to a string for consistency.
301
352
 
302
353
  - If no filetype is specified, filetype is inferred based on
303
- the file extension via a mapping table. (eg. .v is verilog).
304
-
305
-
354
+ the file extension via a mapping table. (eg. .v is verilog).
306
355
  """
307
356
 
308
357
  if fileset is None:
309
- fileset = self.__fileset
358
+ fileset = self._get_active("fileset")
359
+
360
+ if not isinstance(fileset, str):
361
+ raise ValueError("fileset key must be a string")
310
362
 
311
363
  # handle list inputs
312
364
  if isinstance(filename, (list, tuple)):
365
+ params = []
313
366
  for item in filename:
314
- self.add_file(
315
- item,
316
- fileset=fileset,
317
- clobber=clobber,
318
- filetype=filetype)
319
- return
367
+ params.extend(
368
+ self.add_file(
369
+ item,
370
+ fileset=fileset,
371
+ clobber=clobber,
372
+ filetype=filetype))
373
+ return params
320
374
 
321
375
  if filename is None:
322
376
  raise ValueError("add_file cannot process None")
@@ -325,27 +379,24 @@ class DesignSchema(NamedSchema, DependencySchema):
325
379
  filename = str(filename)
326
380
 
327
381
  # map extension to default filetype/fileset
328
-
329
382
  if not filetype:
330
383
  ext = utils.get_file_ext(filename)
331
384
  iomap = utils.get_default_iomap()
332
385
  if ext in iomap:
333
- default_fileset, default_filetype = iomap[ext]
386
+ _, default_filetype = iomap[ext]
334
387
  filetype = default_filetype
335
388
  else:
336
- raise ValueError("illegal file extension")
389
+ raise ValueError(f"Unrecognized file extension: {ext}")
337
390
 
338
- # final error checking
339
- if not fileset or not filetype:
340
- raise SiliconCompilerError(
341
- f'Unable to infer fileset and/or filetype for '
342
- f'{filename} based on file extension.')
391
+ if not dataroot:
392
+ dataroot = self._get_active("package")
343
393
 
344
394
  # adding files to dictionary
345
- if clobber:
346
- return self.set('fileset', fileset, 'file', filetype, filename)
347
- else:
348
- return self.add('fileset', fileset, 'file', filetype, filename)
395
+ with self.active_dataroot(dataroot):
396
+ if clobber:
397
+ return self.set('fileset', fileset, 'file', filetype, filename)
398
+ else:
399
+ return self.add('fileset', fileset, 'file', filetype, filename)
349
400
 
350
401
  ###############################################
351
402
  def get_file(self,
@@ -362,7 +413,7 @@ class DesignSchema(NamedSchema, DependencySchema):
362
413
  """
363
414
 
364
415
  if fileset is None:
365
- fileset = self.__fileset
416
+ fileset = self._get_active("fileset")
366
417
 
367
418
  if not isinstance(fileset, list):
368
419
  fileset = [fileset]
@@ -371,79 +422,174 @@ class DesignSchema(NamedSchema, DependencySchema):
371
422
  filetype = [filetype]
372
423
 
373
424
  filelist = []
374
- for i in fileset:
375
- if not isinstance(i, str):
425
+ for fs in fileset:
426
+ if not isinstance(fs, str):
376
427
  raise ValueError("fileset key must be a string")
377
428
  # handle scalar+list in argument
378
429
  if not filetype:
379
- filetype = list(self.getkeys('fileset', i, 'file'))
430
+ filetype = self.getkeys('fileset', fs, 'file')
380
431
  # grab the files
381
- for j in filetype:
382
- filelist.extend(self.get('fileset', i, 'file', j))
432
+ for ftype in filetype:
433
+ filelist.extend(self.find_files('fileset', fs, 'file', ftype))
383
434
 
384
435
  return filelist
385
436
 
437
+ def __write_flist(self,
438
+ filename: str,
439
+ filesets: List[str],
440
+ depalias: Dict[str, Tuple[NamedSchema, str]]):
441
+ written_cmd = set()
442
+
443
+ with open(filename, "w") as f:
444
+ def write(cmd):
445
+ if cmd in written_cmd:
446
+ f.write(f"// {cmd}\n")
447
+ else:
448
+ written_cmd.add(cmd)
449
+ f.write(f"{cmd}\n")
450
+
451
+ def write_header(header):
452
+ f.write(f"// {header}\n")
453
+
454
+ for lib, fileset in self.get_fileset(filesets, depalias):
455
+ if lib.get('fileset', fileset, 'idir'):
456
+ write_header(f"{lib.name()} / {fileset} / include directories")
457
+ for idir in lib.find_files('fileset', fileset, 'idir'):
458
+ write(f"+incdir+{idir}")
459
+
460
+ if lib.get('fileset', fileset, 'define'):
461
+ write_header(f"{lib.name()} / {fileset} / defines")
462
+ for define in lib.get('fileset', fileset, 'define'):
463
+ write(f"+define+{define}")
464
+
465
+ for filetype in lib.getkeys('fileset', fileset, 'file'):
466
+ if lib.get('fileset', fileset, 'file', filetype):
467
+ write_header(f"{lib.name()} / {fileset} / {filetype} files")
468
+ for file in lib.find_files('fileset', fileset, 'file', filetype):
469
+ write(file)
470
+
471
+ def __map_fileformat(self, path):
472
+ _, ext = os.path.splitext(path)
473
+
474
+ if ext == ".f":
475
+ return "flist"
476
+ else:
477
+ raise ValueError(f"Unable to determine filetype of: {path}")
478
+
386
479
  ###############################################
387
480
  def write_fileset(self,
388
481
  filename: str,
389
482
  fileset: str = None,
390
- fileformat: str = None) -> None:
483
+ fileformat: str = None,
484
+ depalias: Dict[str, Tuple[NamedSchema, str]] = None) -> None:
391
485
  """Exports filesets to a standard formatted text file.
392
486
 
393
487
  Currently supports Verilog `flist` format only.
394
488
  Intended to support other formats in the future.
489
+ Inferred from file extension if not given.
395
490
 
396
491
  Args:
397
492
  filename (str or Path): Output file name.
398
493
  fileset (str or list[str]): Fileset(s) to export.
399
494
  fileformat (str, optional): Export format.
400
-
401
- Inferred from file extension if not given.
402
-
495
+ depalias (dict of schema objects): Map of aliased objects
403
496
  """
404
497
 
405
498
  if filename is None:
406
- raise ValueError("write_fileset() filename cannot be None")
499
+ raise ValueError("filename cannot be None")
407
500
 
408
501
  if fileset is None:
409
- fileset = self.__fileset
502
+ fileset = self._get_active("fileset")
410
503
 
411
504
  if not isinstance(fileset, list):
412
505
  fileset = [fileset]
413
506
 
507
+ for fset in fileset:
508
+ if not isinstance(fset, str):
509
+ raise ValueError("fileset key must be a string")
510
+
414
511
  # file extension lookup
415
512
  if not fileformat:
416
- formats = {}
417
- formats['f'] = 'flist'
418
- fileformat = formats[Path(filename).suffix.strip('.')]
513
+ fileformat = self.__map_fileformat(filename)
419
514
 
420
515
  if fileformat == "flist":
421
- # TODO: handle dependency tree
422
- # TODO: add source info for comments to flist.
423
- with open(filename, "w") as f:
424
- for i in fileset:
425
- if not isinstance(i, str):
426
- raise ValueError("fileset key must be a string")
427
- for j in ['idir', 'define', 'file']:
428
- if j == 'idir':
429
- vals = self.get('fileset', i, 'idir')
430
- cmd = "+incdir+"
431
- elif j == 'define':
432
- vals = self.get('fileset', i, 'define')
433
- cmd = "+define+"
434
- else:
435
- vals = self.get('fileset', i, 'file', 'verilog')
436
- cmd = ""
437
- if vals:
438
- for item in vals:
439
- f.write(f"{cmd}{item}\n")
516
+ self.__write_flist(filename, fileset, depalias)
440
517
  else:
441
- raise ValueError(f"{fileformat} is not supported")
518
+ raise ValueError(f"{fileformat} is not a supported filetype")
519
+
520
+ def __read_flist(self, filename: str, fileset: str):
521
+ # Extract information
522
+ rel_path = os.path.dirname(os.path.abspath(filename))
523
+
524
+ def expand_path(path):
525
+ path = os.path.expandvars(path)
526
+ path = os.path.expanduser(path)
527
+ if os.path.isabs(path):
528
+ return path
529
+ return os.path.join(rel_path, path)
530
+
531
+ include_dirs = []
532
+ defines = []
533
+ files = []
534
+ with utils.sc_open(filename) as f:
535
+ for line in f:
536
+ line = line.strip()
537
+ if not line:
538
+ continue
539
+ if line.startswith("//"):
540
+ continue
541
+ if line.startswith("+incdir+"):
542
+ include_dirs.append(expand_path(line[8:]))
543
+ elif line.startswith("+define+"):
544
+ defines.append(os.path.expandvars(line[8:]))
545
+ else:
546
+ files.append(expand_path(line))
547
+
548
+ # Create dataroots
549
+ all_paths = include_dirs + [os.path.dirname(f) for f in files]
550
+ all_paths = sorted(set(all_paths))
551
+
552
+ dataroot_root_name = f'flist-{self.name()}-{fileset}-{os.path.basename(filename)}'
553
+ dataroots = {}
554
+
555
+ for path_dir in all_paths:
556
+ found = False
557
+ for pdir in dataroots:
558
+ if path_dir.startswith(pdir):
559
+ found = True
560
+ break
561
+ if not found:
562
+ dataroot_name = f"{dataroot_root_name}-{len(dataroots)}"
563
+ self.set_dataroot(dataroot_name, path_dir)
564
+ dataroots[path_dir] = dataroot_name
565
+
566
+ def get_dataroot(path):
567
+ for pdir, name in dataroots.items():
568
+ if path.startswith(pdir):
569
+ return name, pdir
570
+ return None, None
571
+
572
+ # Assign data
573
+ with self.active_fileset(fileset):
574
+ if defines:
575
+ self.add_define(defines)
576
+ if include_dirs:
577
+ for dir in include_dirs:
578
+ dataroot_name, pdir = get_dataroot(dir)
579
+ if dataroot_name:
580
+ dir = os.path.relpath(dir, pdir)
581
+ self.add_idir(dir, dataroot=dataroot_name)
582
+ if files:
583
+ for f in files:
584
+ dataroot_name, pdir = get_dataroot(f)
585
+ if dataroot_name:
586
+ f = os.path.relpath(f, pdir)
587
+ self.add_file(f, dataroot=dataroot_name)
442
588
 
443
589
  ################################################
444
590
  def read_fileset(self,
445
591
  filename: str,
446
- fileset: str,
592
+ fileset: str = None,
447
593
  fileformat=None) -> None:
448
594
  """Imports filesets from a standard formatted text file.
449
595
 
@@ -454,31 +600,31 @@ class DesignSchema(NamedSchema, DependencySchema):
454
600
  filename (str or Path): Output file name.
455
601
  fileset (str or list[str]): Filesets to import.
456
602
  fileformat (str, optional): Export format.
457
-
458
603
  """
459
604
 
460
605
  if filename is None:
461
- raise ValueError("read_fileset() filename cannot be None")
606
+ raise ValueError("filename cannot be None")
462
607
 
463
608
  if not fileformat:
464
- formats = {}
465
- formats['f'] = 'flist'
466
- fileformat = formats[Path(filename).suffix.strip('.')]
609
+ fileformat = self.__map_fileformat(filename)
610
+
611
+ if fileset is None:
612
+ fileset = self._get_active("fileset")
467
613
 
468
614
  if fileformat == "flist":
469
- raise NotImplementedError("read_fileset is not implemented yet")
615
+ self.__read_flist(filename, fileset)
470
616
  else:
471
- raise ValueError(f"{fileformat} is not supported")
617
+ raise ValueError(f"{fileformat} is not a supported filetype")
472
618
 
473
619
  ################################################
474
620
  # Helper Functions
475
621
  ################################################
476
- def __set_add(self, fileset, option, value, clobber=False, typelist=None):
622
+ def __set_add(self, fileset, option, value, clobber=False, typelist=None, dataroot=None):
477
623
  '''Sets a parameter value in schema.
478
624
  '''
479
625
 
480
626
  if fileset is None:
481
- fileset = self.__fileset
627
+ fileset = self._get_active("fileset")
482
628
 
483
629
  # check for a legal fileset
484
630
  if not fileset or not isinstance(fileset, str):
@@ -496,19 +642,27 @@ class DesignSchema(NamedSchema, DependencySchema):
496
642
  if value is None:
497
643
  raise ValueError(f"None is an illegal {option} value")
498
644
 
499
- if list in typelist and not clobber:
500
- return self.add('fileset', fileset, option, value)
501
- else:
502
- return self.set('fileset', fileset, option, value)
645
+ if not dataroot:
646
+ dataroot = self._get_active("package")
503
647
 
504
- def __get(self, fileset, option):
648
+ with self.active_dataroot(dataroot):
649
+ if list in typelist and not clobber:
650
+ params = self.add('fileset', fileset, option, value)
651
+ else:
652
+ params = self.set('fileset', fileset, option, value)
653
+
654
+ return params
655
+
656
+ def __get(self, fileset, option, is_file=False):
505
657
  '''Gets a parameter value from schema.
506
658
  '''
507
659
  if fileset is None:
508
- fileset = self.__fileset
660
+ fileset = self._get_active("fileset")
509
661
 
510
662
  if not isinstance(fileset, str):
511
663
  raise ValueError("fileset key must be a string")
664
+ if is_file:
665
+ return self.find_files('fileset', fileset, option)
512
666
  return self.get('fileset', fileset, option)
513
667
 
514
668
  @contextlib.contextmanager
@@ -533,9 +687,57 @@ class DesignSchema(NamedSchema, DependencySchema):
533
687
  if not fileset:
534
688
  raise ValueError("fileset cannot be an empty string")
535
689
 
536
- self.__fileset = fileset
537
- yield
538
- self.__fileset = None
690
+ with self._active(fileset=fileset):
691
+ yield
692
+
693
+ def get_fileset(self,
694
+ filesets: Union[List[str], str],
695
+ alias: Dict[str, Tuple[NamedSchema, str]] = None) -> \
696
+ List[Tuple[NamedSchema, str]]:
697
+ """
698
+ Computes the filesets this object required for a given set of filesets
699
+
700
+ Args:
701
+ filesets (list of str): List of filesets to evaluate
702
+ alias (dict of schema objects): Map of aliased objects
703
+
704
+ Returns:
705
+ List of tuples (dependency object, fileset)
706
+ """
707
+ if alias is None:
708
+ alias = {}
709
+
710
+ if isinstance(filesets, str):
711
+ # Ensure we have a list
712
+ filesets = [filesets]
713
+
714
+ mapping = []
715
+ for fileset in filesets:
716
+ if not self.valid("fileset", fileset):
717
+ raise ValueError(f"{fileset} is not defined in {self.name()}")
718
+
719
+ mapping.append((self, fileset))
720
+ for dep, depfileset in self.get("fileset", fileset, "depfileset"):
721
+ if (dep, depfileset) in alias:
722
+ dep_obj, new_depfileset = alias[(dep, depfileset)]
723
+ if dep_obj is None:
724
+ continue
725
+
726
+ if new_depfileset:
727
+ depfileset = new_depfileset
728
+ else:
729
+ dep_obj = self.get_dep(dep)
730
+ if not isinstance(dep_obj, DesignSchema):
731
+ raise TypeError(f"{dep} must be a design object.")
732
+
733
+ mapping.extend(dep_obj.get_fileset(depfileset, alias))
734
+
735
+ # Cleanup
736
+ final_map = []
737
+ for cmap in mapping:
738
+ if cmap not in final_map:
739
+ final_map.append(cmap)
740
+ return final_map
539
741
 
540
742
 
541
743
  ###########################################################################
@@ -632,8 +834,7 @@ def schema_design(schema):
632
834
  help=trim("""
633
835
  Specifies directories to scan for libraries provided with the
634
836
  :keypath:`lib` parameter. If multiple paths are provided, they are
635
- searched based on the order of the libdir list. The libdir
636
- parameter is translated to the '-y' option in verilog based tools.""")))
837
+ searched based on the order of the libdir list.""")))
637
838
 
638
839
  schema.insert(
639
840
  'fileset', fileset, 'lib',
@@ -662,3 +863,13 @@ def schema_design(schema):
662
863
  data literals. The types of parameters and values supported is tightly
663
864
  coupled to tools being used. For example, in Verilog only integer
664
865
  literals (64'h4, 2'b0, 4) and strings are supported.""")))
866
+
867
+ schema.insert(
868
+ 'fileset', fileset, 'depfileset',
869
+ Parameter(
870
+ '[(str,str)]',
871
+ scope=Scope.GLOBAL,
872
+ shorthelp="Design dependency fileset",
873
+ example=[
874
+ "api: chip.set('fileset', 'rtl, 'depfileset', ('lambdalib', 'rtl')"],
875
+ help=trim("""Sets the mapping for dependency filesets.""")))