siliconcompiler 0.30.0__py3-none-any.whl → 0.31.1__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 (79) hide show
  1. siliconcompiler/_metadata.py +1 -1
  2. siliconcompiler/apps/sc_install.py +26 -4
  3. siliconcompiler/apps/sc_remote.py +1 -3
  4. siliconcompiler/core.py +24 -9
  5. siliconcompiler/flowgraph.py +11 -23
  6. siliconcompiler/{package.py → package/__init__.py} +62 -176
  7. siliconcompiler/package/git.py +81 -0
  8. siliconcompiler/package/https.py +93 -0
  9. siliconcompiler/remote/schema.py +9 -8
  10. siliconcompiler/report/report.py +4 -3
  11. siliconcompiler/scheduler/__init__.py +127 -113
  12. siliconcompiler/scheduler/docker_runner.py +4 -4
  13. siliconcompiler/scheduler/run_node.py +3 -3
  14. siliconcompiler/scheduler/send_messages.py +1 -1
  15. siliconcompiler/schema/schema_cfg.py +367 -357
  16. siliconcompiler/schema/schema_obj.py +39 -29
  17. siliconcompiler/schema/utils.py +19 -0
  18. siliconcompiler/sphinx_ext/schemagen.py +3 -1
  19. siliconcompiler/templates/replay/replay.sh.j2 +92 -0
  20. siliconcompiler/templates/tcl/manifest.tcl.j2 +1 -1
  21. siliconcompiler/tools/_common/__init__.py +8 -2
  22. siliconcompiler/tools/_common/asic.py +1 -1
  23. siliconcompiler/tools/_common/tcl/sc_pin_constraints.tcl +3 -5
  24. siliconcompiler/tools/genfasm/genfasm.py +1 -1
  25. siliconcompiler/tools/klayout/export.py +5 -0
  26. siliconcompiler/tools/klayout/klayout.py +18 -1
  27. siliconcompiler/tools/klayout/klayout_export.py +4 -1
  28. siliconcompiler/tools/klayout/klayout_operations.py +5 -2
  29. siliconcompiler/tools/klayout/klayout_utils.py +23 -0
  30. siliconcompiler/tools/klayout/operations.py +5 -0
  31. siliconcompiler/tools/magic/magic.py +1 -1
  32. siliconcompiler/tools/openroad/_apr.py +14 -3
  33. siliconcompiler/tools/openroad/antenna_repair.py +2 -1
  34. siliconcompiler/tools/openroad/clock_tree_synthesis.py +2 -1
  35. siliconcompiler/tools/openroad/detailed_placement.py +2 -1
  36. siliconcompiler/tools/openroad/detailed_route.py +8 -0
  37. siliconcompiler/tools/openroad/fillercell_insertion.py +2 -1
  38. siliconcompiler/tools/openroad/global_placement.py +2 -1
  39. siliconcompiler/tools/openroad/pin_placement.py +2 -1
  40. siliconcompiler/tools/openroad/rdlroute.py +4 -0
  41. siliconcompiler/tools/openroad/repair_design.py +2 -1
  42. siliconcompiler/tools/openroad/repair_timing.py +2 -1
  43. siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +6 -0
  44. siliconcompiler/tools/openroad/scripts/apr/sc_clock_tree_synthesis.tcl +1 -0
  45. siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +8 -0
  46. siliconcompiler/tools/openroad/scripts/apr/sc_global_placement.tcl +1 -0
  47. siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +2 -0
  48. siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +3 -3
  49. siliconcompiler/tools/openroad/scripts/apr/sc_macro_placement.tcl +5 -0
  50. siliconcompiler/tools/openroad/scripts/apr/sc_power_grid.tcl +1 -0
  51. siliconcompiler/tools/openroad/scripts/apr/sc_repair_design.tcl +1 -0
  52. siliconcompiler/tools/openroad/scripts/apr/sc_repair_timing.tcl +3 -0
  53. siliconcompiler/tools/openroad/scripts/common/procs.tcl +29 -12
  54. siliconcompiler/tools/openroad/scripts/common/reports.tcl +15 -0
  55. siliconcompiler/tools/openroad/scripts/common/write_images.tcl +28 -0
  56. siliconcompiler/tools/openroad/scripts/sc_rdlroute.tcl +3 -13
  57. siliconcompiler/tools/vpr/vpr.py +86 -6
  58. siliconcompiler/tools/yosys/__init__.py +7 -0
  59. siliconcompiler/tools/yosys/sc_syn.tcl +33 -24
  60. siliconcompiler/tools/yosys/syn_asic.py +27 -0
  61. siliconcompiler/tools/yosys/syn_asic.tcl +27 -0
  62. siliconcompiler/toolscripts/_tools.json +15 -3
  63. siliconcompiler/toolscripts/rhel8/install-yosys-moosic.sh +17 -0
  64. siliconcompiler/toolscripts/rhel8/install-yosys-slang.sh +22 -0
  65. siliconcompiler/toolscripts/rhel9/install-yosys-moosic.sh +17 -0
  66. siliconcompiler/toolscripts/rhel9/install-yosys-slang.sh +22 -0
  67. siliconcompiler/toolscripts/ubuntu20/install-yosys-moosic.sh +17 -0
  68. siliconcompiler/toolscripts/ubuntu20/install-yosys-slang.sh +22 -0
  69. siliconcompiler/toolscripts/ubuntu22/install-yosys-moosic.sh +17 -0
  70. siliconcompiler/toolscripts/ubuntu22/install-yosys-slang.sh +22 -0
  71. siliconcompiler/toolscripts/ubuntu24/install-yosys-moosic.sh +17 -0
  72. siliconcompiler/toolscripts/ubuntu24/install-yosys-slang.sh +22 -0
  73. siliconcompiler/utils/__init__.py +33 -5
  74. {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/METADATA +21 -23
  75. {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/RECORD +79 -66
  76. {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/WHEEL +1 -1
  77. {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/entry_points.txt +4 -0
  78. {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/LICENSE +0 -0
  79. {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/top_level.txt +0 -0
@@ -39,7 +39,7 @@ except ImportError:
39
39
  _has_yaml = False
40
40
 
41
41
  from .schema_cfg import schema_cfg
42
- from .utils import escape_val_tcl, PACKAGE_ROOT, translate_loglevel
42
+ from .utils import escape_val_tcl, PACKAGE_ROOT, translate_loglevel, PerNode, Scope
43
43
 
44
44
 
45
45
  class Schema:
@@ -287,7 +287,7 @@ class Schema:
287
287
  try:
288
288
  return cfg['node'][step][index][field]
289
289
  except KeyError:
290
- if cfg['pernode'] == 'required':
290
+ if PerNode(cfg['pernode']) == PerNode.REQUIRED:
291
291
  return cfg['node']['default']['default'][field]
292
292
 
293
293
  try:
@@ -300,6 +300,10 @@ class Schema:
300
300
  except KeyError:
301
301
  return cfg['node']['default']['default'][field]
302
302
  elif field in cfg:
303
+ if field == "pernode":
304
+ return PerNode(cfg[field])
305
+ if field == "scope":
306
+ return Scope(cfg[field])
303
307
  return cfg[field]
304
308
  else:
305
309
  raise ValueError(f'Invalid field {field}')
@@ -621,7 +625,7 @@ class Schema:
621
625
  has_global = True
622
626
  vals.append((cfg['node'][step][index]['value'], step_arg, index_arg))
623
627
 
624
- if (cfg['pernode'] != 'required') and not has_global and return_defvalue:
628
+ if (PerNode(cfg['pernode']) != PerNode.REQUIRED) and not has_global and return_defvalue:
625
629
  vals.append((cfg['node']['default']['default']['value'], None, None))
626
630
 
627
631
  return vals
@@ -707,7 +711,7 @@ class Schema:
707
711
  # ignore history in case of cumulative history
708
712
  if key[0] != 'history':
709
713
  scope = self.get(*key, field='scope')
710
- if not self.is_empty(*key) and (scope == 'job'):
714
+ if not self.is_empty(*key) and (scope == Scope.JOB):
711
715
  self.__copyparam(self.cfg,
712
716
  self.cfg['history'][jobname],
713
717
  key)
@@ -865,15 +869,21 @@ class Schema:
865
869
 
866
870
  if field == 'scope':
867
871
  # Restricted allowed values
868
- if not (isinstance(value, str) and value in ('global', 'job', 'scratch')):
869
- raise TypeError(error_msg('one of "global", "job", or "scratch"'))
872
+ if isinstance(value, Scope):
873
+ return value.value
874
+ scope_values = [val.value for val in Scope]
875
+ if not (isinstance(value, str) and value in scope_values):
876
+ raise TypeError(error_msg(f'one of {", ".join(sorted(scope_values))}'))
870
877
  return value
871
878
 
872
879
  if field == 'pernode':
873
880
  # Restricted allowed values
874
- if not (isinstance(value, str) and value in ('never', 'optional', 'required')):
881
+ if isinstance(value, PerNode):
882
+ return value.value
883
+ pernode_values = [val.value for val in PerNode]
884
+ if not (isinstance(value, str) and value in pernode_values):
875
885
  raise TypeError(f'Invalid value {value} for field {field}: '
876
- 'expected one of "never", "optional", or "required"')
886
+ f'expected one of {", ".join(sorted(pernode_values))}')
877
887
  return value
878
888
 
879
889
  if field in (
@@ -955,10 +965,10 @@ class Schema:
955
965
  return 'step and index are only valid for value fields'
956
966
  return None
957
967
 
958
- if pernode == 'never' and (step is not None or index is not None):
968
+ if PerNode(pernode) == PerNode.NEVER and (step is not None or index is not None):
959
969
  return 'step and index are not valid for this parameter'
960
970
 
961
- if pernode == 'required' and (step is None or index is None):
971
+ if PerNode(pernode) == PerNode.REQUIRED and (step is None or index is None):
962
972
  return 'step and index are required for this parameter'
963
973
 
964
974
  if step is None and index is not None:
@@ -989,11 +999,15 @@ class Schema:
989
999
  if key in cfg:
990
1000
  cfg = cfg[key]
991
1001
  elif 'default' in cfg:
1002
+ cfg_default = cfg['default']
992
1003
  if insert_defaults:
993
- cfg[key] = copy.deepcopy(cfg['default'])
1004
+ if Schema._is_leaf(cfg_default) and cfg_default['lock']:
1005
+ raise ValueError(f'{keypath} is locked and key cannot be added')
1006
+
1007
+ cfg[key] = copy.deepcopy(cfg_default)
994
1008
  cfg = cfg[key]
995
1009
  elif use_default:
996
- cfg = cfg['default']
1010
+ cfg = cfg_default
997
1011
  else:
998
1012
  raise ValueError(f'Invalid keypath {keypath}: unexpected key: {key}')
999
1013
  else:
@@ -1078,15 +1092,19 @@ class Schema:
1078
1092
 
1079
1093
  tcl_set_cmds = []
1080
1094
  for key in self.allkeys():
1095
+ # print out all non default values
1096
+ if 'default' in key:
1097
+ continue
1098
+
1081
1099
  typestr = self.get(*key, field='type')
1082
1100
  pernode = self.get(*key, field='pernode')
1083
1101
 
1084
- if pernode == 'required' and (step is None or index is None):
1102
+ if PerNode(pernode) == PerNode.REQUIRED and (step is None or index is None):
1085
1103
  # Skip mandatory per-node parameters if step and index are not specified
1086
1104
  # TODO: how should we dump these?
1087
1105
  continue
1088
1106
 
1089
- if pernode != 'never':
1107
+ if not pernode.is_never():
1090
1108
  value = self.get(*key, step=step, index=index)
1091
1109
  else:
1092
1110
  value = self.get(*key)
@@ -1096,19 +1114,11 @@ class Schema:
1096
1114
 
1097
1115
  valstr = escape_val_tcl(value, typestr)
1098
1116
 
1099
- # Turning scalars into lists
1100
- if not (typestr.startswith('[') or typestr.startswith('(')):
1101
- valstr = f'[list {valstr}]'
1102
-
1103
- # TODO: Temp fix to get rid of empty args
1117
+ # Ensure empty values get something
1104
1118
  if valstr == '':
1105
- valstr = '[list ]'
1106
-
1107
- outstr = f"{prefix} {keystr} {valstr}"
1119
+ valstr = '{}'
1108
1120
 
1109
- # print out all non default values
1110
- if 'default' not in key:
1111
- tcl_set_cmds.append(outstr)
1121
+ tcl_set_cmds.append(f"{prefix} {keystr} {valstr}")
1112
1122
 
1113
1123
  if template:
1114
1124
  fout.write(template.render(manifest_dict='\n'.join(tcl_set_cmds),
@@ -1515,7 +1525,7 @@ class Schema:
1515
1525
  used_switches.update(switchstrs)
1516
1526
  if typestr == 'bool':
1517
1527
  # Boolean type arguments
1518
- if pernodestr == 'never':
1528
+ if pernodestr.is_never():
1519
1529
  parser.add_argument(*switchstrs,
1520
1530
  nargs='?',
1521
1531
  metavar=metavar,
@@ -1531,7 +1541,7 @@ class Schema:
1531
1541
  action='append',
1532
1542
  help=helpstr,
1533
1543
  default=argparse.SUPPRESS)
1534
- elif '[' in typestr or pernodestr != 'never':
1544
+ elif '[' in typestr or not pernodestr.is_never():
1535
1545
  # list type arguments
1536
1546
  parser.add_argument(*switchstrs,
1537
1547
  metavar=metavar,
@@ -1683,13 +1693,13 @@ class Schema:
1683
1693
  sctype = self.get(*keypath, field='type')
1684
1694
  pernode = self.get(*keypath, field='pernode')
1685
1695
  step, index = None, None
1686
- if pernode == 'required':
1696
+ if PerNode(pernode) == PerNode.REQUIRED:
1687
1697
  try:
1688
1698
  step, index, val = remainder.split(' ', 2)
1689
1699
  except ValueError:
1690
1700
  self.logger.error(f"Invalid value '{item}' for switch {switchstr}. "
1691
1701
  "Requires step and index before final value.")
1692
- elif pernode == 'optional':
1702
+ elif PerNode(pernode) == PerNode.OPTIONAL:
1693
1703
  # Split on spaces, preserving items that are grouped in quotes
1694
1704
  items = shlex.split(remainder)
1695
1705
  if len(items) > 3:
@@ -7,10 +7,29 @@
7
7
  import os
8
8
  import re
9
9
  import sys
10
+ from enum import Enum
10
11
 
11
12
  PACKAGE_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
12
13
 
13
14
 
15
+ #############################################################################
16
+ # ENUM DEFINITIONs
17
+ #############################################################################
18
+ class Scope(Enum):
19
+ GLOBAL = 'global'
20
+ JOB = 'job'
21
+ SCRATCH = 'scratch'
22
+
23
+
24
+ class PerNode(Enum):
25
+ NEVER = 'never'
26
+ OPTIONAL = 'optional'
27
+ REQUIRED = 'required'
28
+
29
+ def is_never(self):
30
+ return self == PerNode.NEVER
31
+
32
+
14
33
  def escape_val_tcl(val, typestr):
15
34
  '''Recursive helper function for converting Python values to safe TCL
16
35
  values, based on the SC type string.'''
@@ -37,9 +37,11 @@ class SchemaGen(SphinxDirective):
37
37
  entries = [[strong('Description'), para(schema['shorthelp'])],
38
38
  [strong('Type'), para(schema['type'])]]
39
39
 
40
- if schema['pernode'] != 'never':
40
+ if schema['pernode'] != utils.PerNode.NEVER:
41
41
  entries.append([strong('Per step/index'), para(schema['pernode'])])
42
42
 
43
+ entries.append([strong('Scope'), para(schema['scope'])])
44
+
43
45
  if 'enum' in schema['type']:
44
46
  entries.append([strong('Allowed Values'),
45
47
  build_list([code(val) for val in schema['enum']])])
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env bash
2
+ if [ "${BASH_SOURCE[0]}" != "$0" ]; then
3
+ echo "${BASH_SOURCE[0]} must be executed."
4
+ return
5
+ fi
6
+
7
+ # Parse replay arguments
8
+ CD_WORK="{{ work_dir }}"
9
+ PRINT=""
10
+ CMDPREFIX=""
11
+ SKIPEXPORT=0
12
+ while [[ $# -gt 0 ]]; do
13
+ case $1 in
14
+ --which)
15
+ PRINT="which"
16
+ shift
17
+ ;;
18
+ --version)
19
+ PRINT="version"
20
+ shift
21
+ ;;
22
+ --directory)
23
+ PRINT="directory"
24
+ shift
25
+ ;;
26
+ --command)
27
+ PRINT="command"
28
+ shift
29
+ ;;
30
+ --skipcd)
31
+ CD_WORK="."
32
+ shift
33
+ ;;
34
+ --skipexports)
35
+ SKIPEXPORT=1
36
+ shift
37
+ ;;
38
+ --cmdprefix)
39
+ CMDPREFIX="$2"
40
+ shift
41
+ shift
42
+ ;;
43
+ -h|--help)
44
+ echo "Usage: $0"
45
+ echo " Options:"
46
+ echo " --which print which executable would be used"
47
+ echo " --version print the version of the executable, if supported"
48
+ echo " --directory print the execution directory"
49
+ echo " --command print the execution command"
50
+ echo " --skipcd do not change directory into replay directory"
51
+ echo " --skipexports do not export environmental variables"
52
+ echo " --cmdprefix <cmd> prefix to add to the replay command, such as gdb"
53
+ echo " -h,--help print this help"
54
+ exit 0
55
+ ;;
56
+ *)
57
+ echo "Unknown option $1"
58
+ exit 1
59
+ ;;
60
+ esac
61
+ done
62
+
63
+ if [ $SKIPEXPORT == 0 ]; then
64
+ # Environmental variables{% for key, value in exports.items() %}
65
+ export {{ key }}="{{ value }}"{% endfor %}
66
+ fi
67
+
68
+ # Switch to the working directory
69
+ cd "$CD_WORK"
70
+
71
+ case $PRINT in
72
+ "which")
73
+ which {{ executable }}
74
+ exit 0
75
+ ;;
76
+ "version")
77
+ {% if version_flag is defined %}{{ executable }} {{ version_flag }}{% else %}echo "--version is not supported"{% endif %}
78
+ exit 0
79
+ ;;
80
+ "directory")
81
+ echo "Working directory: $PWD"
82
+ exit 0
83
+ ;;
84
+ "command")
85
+ echo "{% for cmd in cmds %}{% if not loop.first %} {% endif %}{{ cmd }}{% endfor %}"
86
+ exit 0
87
+ ;;
88
+ esac
89
+
90
+ # Command execution
91
+ $CMDPREFIX \{% for cmd in cmds %}
92
+ {% if not loop.first %} {% endif %}{{ cmd }}{% if not loop.last %} \{% endif %}{% endfor %}
@@ -34,7 +34,7 @@ proc sc_cfg_exists { args } {
34
34
 
35
35
  proc sc_top {} {
36
36
  set sc_entrypoint [sc_cfg_get option entrypoint]
37
- if {$sc_entrypoint == ""} {
37
+ if {$sc_entrypoint == {{ '{}' }}} {
38
38
  return [sc_cfg_get design]
39
39
  }
40
40
  return $sc_entrypoint
@@ -161,7 +161,7 @@ def __get_step_index(chip, *key):
161
161
  step = chip.get('arg', 'step')
162
162
  index = chip.get('arg', 'index')
163
163
 
164
- if chip.get(*key, field='pernode') == 'never':
164
+ if chip.get(*key, field='pernode').is_never():
165
165
  step = None
166
166
  index = None
167
167
 
@@ -330,7 +330,7 @@ def pick_key(chip, check_keys, step=None, index=None):
330
330
  check_step = step
331
331
  check_index = index
332
332
 
333
- if chip.get(*key, field='pernode') == 'never':
333
+ if chip.get(*key, field='pernode').is_never():
334
334
  check_step = None
335
335
  check_index = None
336
336
 
@@ -347,9 +347,15 @@ def input_provides(chip, step, index, flow=None):
347
347
  if not flow:
348
348
  flow = chip.get('option', 'flow')
349
349
 
350
+ pruned_nodes = chip.get('option', 'prune')
351
+
350
352
  nodes = chip.get('flowgraph', flow, step, index, 'input')
351
353
  inputs = {}
352
354
  for in_step, in_index in nodes:
355
+ if (in_step, in_index) in pruned_nodes:
356
+ # node has been pruned so will not provide anything
357
+ continue
358
+
353
359
  if chip.get('record', 'status', step=in_step, index=in_index) == \
354
360
  NodeStatus.SKIPPED:
355
361
  for file, nodes in input_provides(chip, in_step, in_index, flow=flow).items():
@@ -188,7 +188,7 @@ def set_tool_task_lib_var(chip,
188
188
  get_step = step
189
189
  get_index = index
190
190
 
191
- if chip.get(*lib_key, field='pernode') == 'never':
191
+ if chip.get(*lib_key, field='pernode').is_never():
192
192
  get_step = None
193
193
  get_index = None
194
194
 
@@ -15,22 +15,20 @@ proc sc_collect_pin_constraints {
15
15
  set side [dict get $params side]
16
16
  set place [dict get $params placement]
17
17
 
18
- if { [llength $place] != 0 } {
18
+ if { $place != {} } {
19
19
  # Pin has placement information
20
- if { [llength $order] != 0 } {
20
+ if { $order != {} } {
21
21
  # Pin also has order information
22
22
  $print_func "Pin $name has placement specified in constraints, but also order."
23
23
  }
24
24
  lappend placement_pins $name
25
25
  } else {
26
26
  # Pin doesn't have placement
27
- if { [llength $side] == 0 || [llength $order] == 0 } {
27
+ if { $side == {} || $order == {} } {
28
28
  # Pin information is incomplete
29
29
  $print_func \
30
30
  "Warning: Pin $name doesn't have enough information to perform placement."
31
31
  } else {
32
- set side [lindex $side 0]
33
- set order [lindex $order 0]
34
32
  if { ![dict exists $pin_order $side $order] } {
35
33
  dict set pin_order $side $order []
36
34
  }
@@ -24,7 +24,7 @@ def make_docs(chip):
24
24
  def setup(chip):
25
25
  chip.set('tool', 'genfasm', 'exe', 'genfasm', clobber=False)
26
26
  chip.set('tool', 'genfasm', 'vswitch', '--version')
27
- chip.set('tool', 'genfasm', 'version', '>=8.1.0', clobber=False)
27
+ chip.set('tool', 'genfasm', 'version', '>=9.0.0', clobber=False)
28
28
 
29
29
  add_tool_requirements(chip)
30
30
 
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  from siliconcompiler.tools.klayout.klayout import setup as setup_tool
3
+ from siliconcompiler.tools.klayout.klayout import process_metrics
3
4
  from siliconcompiler.tools.klayout.klayout import runtime_options as runtime_options_tool
4
5
  from siliconcompiler.tools.klayout.screenshot import setup_gui_screenshot
5
6
  from siliconcompiler.tools._common import input_provides, get_tool_task
@@ -127,3 +128,7 @@ def runtime_options(chip):
127
128
  return runtime_options_tool(chip) + [
128
129
  '-rd', f'SC_TOOLS_ROOT={os.path.dirname(os.path.dirname(__file__))}'
129
130
  ]
131
+
132
+
133
+ def post_process(chip):
134
+ process_metrics(chip)
@@ -12,9 +12,11 @@ Installation: https://www.klayout.de/build.html
12
12
 
13
13
  import os
14
14
  from pathlib import Path
15
+ import json
15
16
  import platform
16
17
  import shutil
17
- from siliconcompiler.tools._common import get_tool_task
18
+ from siliconcompiler.tools._common import get_tool_task, record_metric
19
+ from siliconcompiler import sc_open
18
20
 
19
21
 
20
22
  ####################################################################
@@ -113,6 +115,21 @@ def parse_version(stdout):
113
115
  return stdout.split()[1]
114
116
 
115
117
 
118
+ def process_metrics(chip):
119
+ metrics_file = "reports/metrics.json"
120
+ if not os.path.exists(metrics_file):
121
+ return
122
+
123
+ step = chip.get('arg', 'step')
124
+ index = chip.get('arg', 'index')
125
+
126
+ with sc_open(metrics_file) as f:
127
+ metrics = json.load(f)
128
+
129
+ if "area" in metrics:
130
+ record_metric(chip, step, index, "totalarea", metrics["area"], metrics_file, "um^2")
131
+
132
+
116
133
  ##################################################
117
134
  if __name__ == "__main__":
118
135
 
@@ -127,7 +127,8 @@ def main():
127
127
  technology,
128
128
  get_streams,
129
129
  save_technology,
130
- get_schema
130
+ get_schema,
131
+ generate_metrics
131
132
  )
132
133
  from klayout_show import show
133
134
  from _common.asic import get_libraries
@@ -205,6 +206,8 @@ def main():
205
206
  # Save tech files
206
207
  save_technology(design, sc_tech)
207
208
 
209
+ generate_metrics()
210
+
208
211
 
209
212
  if __name__ == '__main__':
210
213
  main()
@@ -25,7 +25,7 @@ def __get_keypath_step_index(schema, *keypath):
25
25
  'index': schema.get('arg', 'index')
26
26
  }
27
27
  pernode = schema.get(*keypath, field='pernode')
28
- if pernode == 'never':
28
+ if pernode.is_never():
29
29
  ret['step'] = None
30
30
  ret['index'] = None
31
31
 
@@ -330,7 +330,8 @@ if __name__ == "__main__":
330
330
  from klayout_utils import (
331
331
  technology,
332
332
  get_streams,
333
- get_schema
333
+ get_schema,
334
+ generate_metrics
334
335
  )
335
336
 
336
337
  schema = get_schema(manifest='sc_manifest.json')
@@ -362,3 +363,5 @@ if __name__ == "__main__":
362
363
  parse_operations(schema, base_layout, sc_klayout_ops)
363
364
 
364
365
  write_stream(base_layout, out_gds, __with_timestamps(schema))
366
+
367
+ generate_metrics()
@@ -1,5 +1,6 @@
1
1
  import pya
2
2
  import importlib.util as importlib_util
3
+ import json
3
4
  import os
4
5
  import shutil
5
6
  import sys
@@ -180,3 +181,25 @@ def get_schema(manifest):
180
181
  spec.loader.exec_module(module)
181
182
  # Return schema
182
183
  return module.Schema(manifest=manifest)
184
+
185
+
186
+ def generate_metrics():
187
+ metrics = {}
188
+
189
+ main_window = pya.MainWindow.instance()
190
+ if not main_window:
191
+ return
192
+ layout_view = main_window.current_view()
193
+ if not layout_view:
194
+ return
195
+ cell_view = layout_view.active_cellview()
196
+ if not cell_view:
197
+ return
198
+ cell = cell_view.cell
199
+ if not cell:
200
+ return
201
+
202
+ metrics["area"] = cell.dbbox().area()
203
+
204
+ with open('reports/metrics.json', 'w') as f:
205
+ json.dump(metrics, f, indent=2)
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  from siliconcompiler import SiliconCompilerError
3
3
  from siliconcompiler.tools.klayout.klayout import setup as setup_tool
4
+ from siliconcompiler.tools.klayout.klayout import process_metrics
4
5
  from siliconcompiler.tools.klayout.klayout import runtime_options as runtime_options_tool
5
6
  from siliconcompiler.tools._common import input_provides, get_tool_task
6
7
 
@@ -202,3 +203,7 @@ def runtime_options(chip):
202
203
  return runtime_options_tool(chip) + [
203
204
  '-rd', f'SC_TOOLS_ROOT={os.path.dirname(os.path.dirname(__file__))}'
204
205
  ]
206
+
207
+
208
+ def post_process(chip):
209
+ process_metrics(chip)
@@ -87,7 +87,7 @@ def process_file(file_type, chip, *key):
87
87
  index = chip.get('arg', 'index')
88
88
  tool, task = get_tool_task(chip, step, index)
89
89
 
90
- if chip.get(*key, field='pernode') == 'never':
90
+ if chip.get(*key, field='pernode').is_never():
91
91
  files = chip.find_files(*key)
92
92
  else:
93
93
  files = chip.find_files(*key, step=step, index=index)
@@ -197,13 +197,15 @@ def extract_metrics(chip):
197
197
  has_timing = metrics['sc__metric__timing__clocks'] > 0
198
198
 
199
199
  for metric, or_metric, or_use, or_unit in [
200
+ ('vias', 'sc__step__global_route__vias', True, None),
200
201
  ('vias', 'sc__step__route__vias', True, None),
202
+ ('wirelength', 'sc__step__global_route__wirelength', True, 'distance'),
201
203
  ('wirelength', 'sc__step__route__wirelength', True, 'distance'),
202
204
  ('cellarea', 'sc__metric__design__instance__area', True, 'area'),
203
205
  ('stdcellarea', 'sc__metric__design__instance__area__stdcell', True, 'area'),
204
206
  ('macroarea', 'sc__metric__design__instance__area__macros', True, 'area'),
205
207
  ('padcellarea', 'sc__metric__design__instance__area__padcells', True, 'area'),
206
- ('totalarea', 'sc__metric__design__core__area', True, 'area'),
208
+ ('totalarea', 'sc__metric__design__die__area', True, 'area'),
207
209
  ('utilization', 'sc__metric__design__instance__utilization', True, 100.0),
208
210
  ('setuptns', 'sc__metric__timing__setup__tns', has_timing, 'time'),
209
211
  ('holdtns', 'sc__metric__timing__hold__tns', has_timing, 'time'),
@@ -763,6 +765,10 @@ def define_drt_params(chip):
763
765
  schelp='list of layers to treat as unidirectional regardless of '
764
766
  'what the tech lef specifies')
765
767
 
768
+ set_tool_task_var(chip, param_key='drt_report_interval',
769
+ default_value=5,
770
+ schelp='reporting interval in steps for generating a DRC report.')
771
+
766
772
 
767
773
  def define_sta_params(chip):
768
774
  step = chip.get('arg', 'step')
@@ -868,6 +874,10 @@ def define_mpl_params(chip):
868
874
  set_tool_task_var(chip, param_key='rtlmp_fence_weight',
869
875
  schelp='Weight for fence cost, or how far the macro is from zero '
870
876
  'fence violation')
877
+ set_tool_task_var(chip, param_key='rtlmp_boundary_weight',
878
+ schelp='Weight for the boundary, or how far the hard macro clusters are from '
879
+ 'boundaries. Note that mixed macro clusters are not pushed, thus not '
880
+ 'considered in this cost.')
871
881
  set_tool_task_var(chip, param_key='rtlmp_blockage_weight',
872
882
  schelp='Weight for the boundary, or how far the hard macro clusters are '
873
883
  'from boundaries')
@@ -980,7 +990,8 @@ def set_reports(chip, reports):
980
990
  "ir_drop",
981
991
  "clock_placement",
982
992
  "clock_trees",
983
- "optimization_placement"
993
+ "optimization_placement",
994
+ "module_view"
984
995
  )
985
996
 
986
997
  chip.set('tool', tool, 'task', task, 'var', 'reports',
@@ -994,7 +1005,7 @@ def set_reports(chip, reports):
994
1005
  for key in (('tool', tool, 'task', task, 'var', f'skip_{type}'),
995
1006
  ('option', 'var', f'openroad_skip_{type}')):
996
1007
  if chip.valid(*key):
997
- if chip.get(*key, field='pernode') == 'never':
1008
+ if chip.get(*key, field='pernode').is_never():
998
1009
  if chip.get(*key) == ["true"]:
999
1010
  return False
1000
1011
  elif chip.get(*key, step=step, index=index) == ["true"]:
@@ -54,7 +54,8 @@ def setup(chip):
54
54
  'power_density',
55
55
  'optimization_placement',
56
56
  'clock_placement',
57
- 'clock_trees'
57
+ 'clock_trees',
58
+ 'module_view'
58
59
  ])
59
60
 
60
61
 
@@ -51,7 +51,8 @@ def setup(chip):
51
51
  'power_density',
52
52
  'optimization_placement',
53
53
  'clock_placement',
54
- 'clock_trees'
54
+ 'clock_trees',
55
+ 'module_view'
55
56
  ])
56
57
 
57
58
 
@@ -46,7 +46,8 @@ def setup(chip):
46
46
  'placement_density',
47
47
  'routing_congestion',
48
48
  'power_density',
49
- 'optimization_placement'
49
+ 'optimization_placement',
50
+ 'module_view'
50
51
  ])
51
52
 
52
53