files: # Copyright 2020 Silicon Compiler Authors. All Rights Reserved.
  
  import argparse
  import base64
  import time
  import datetime
  import multiprocessing
  import tarfile
  import traceback
  import asyncio
  from subprocess import run, PIPE
  import os
  import glob
  import pathlib
  import sys
  import gzip
  import re
  import json
  import logging
  import hashlib
  import shutil
  import copy
  import importlib
  import textwrap
  import math
  import pandas
  import yaml
  import graphviz
  import time
  import uuid
  import shlex
  import platform
  import getpass
  import csv
  import distro
  import netifaces
  import webbrowser
  import packaging.version
  import packaging.specifiers
  from jinja2 import Environment, FileSystemLoader
  from pathlib import Path
  from timeit import default_timer as timer
  from siliconcompiler.client import *
  from siliconcompiler.schema import *
  from siliconcompiler.scheduler import _deferstep
  from siliconcompiler import leflib
  from siliconcompiler import utils
  from siliconcompiler import _metadata
  import psutil
  
  class TaskStatus():
      # Could use Python 'enum' class here, but that doesn't work nicely with
      # schema.
      PENDING = 'pending'
      SUCCESS = 'success'
      ERROR = 'error'
  
  class Chip:
      """Object for configuring and executing hardware design flows.
  
      This is the main object used for configuration, data, and
      execution within the SiliconCompiler platform.
  
      Args:
          design (string): Name of the top level chip design module.
  
      Examples:
          >>> siliconcompiler.Chip(design="top")
          Creates a chip object with name "top".
      """
  
      ###########################################################################
      def __init__(self, design, loglevel=None):
          # version numbers
          self.scversion = _metadata.version
          self.schemaversion = SCHEMA_VERSION
  
          # Local variables
          self.scroot = os.path.dirname(os.path.abspath(__file__))
          self.cwd = os.getcwd()
          self.error = 0
          self.cfg = schema_cfg()
          # The 'status' dictionary can be used to store ephemeral config values.
          # Its contents will not be saved, and can be set by parent scripts
          # such as a web server or supervisor process. Currently supported keys:
          # * 'jobhash': A hash or UUID which can identify jobs in a larger system.
          # * 'remote_cfg': Dictionary containing remote server configurations
          #                 (address, credentials, etc.)
          # * 'slurm_account': User account ID in a connected slurm HPC cluster.
          # * 'slurm_partition': Name of the partition in which a task should run
          #                      on a connected slurm HPC cluster.
          # * 'watchdog': Activity-monitoring semaphore for jobs scheduled on an
          #               HPC cluster; expects a 'threading.Event'-like object.
          # * 'max_fs_bytes': A limit on how much disk space a job is allowed
          #                   to consume in a connected HPC cluster's storage.
          self.status = {}
  
          self.builtin = ['minimum','maximum',
                          'nop', 'mux', 'join', 'verify']
  
          # We set 'design' and 'loglevel' directly in the config dictionary
          # because of a chicken-and-egg problem: self.set() relies on the logger,
          # but the logger relies on these values.
          self.cfg['design']['value'] = design
          if loglevel:
              self.cfg['option']['loglevel']['value'] = loglevel
  
          self._init_logger()
  
          self._loaded_modules = {
              'flows': [],
              'pdks': [],
              'libs': [],
              'checklists': []
          }
  
      ###########################################################################
      @property
      def design(self):
          '''Design name of chip object.
  
          This is an immutable property.'''
          return self.get('design')
  
      ###########################################################################
      def _init_logger(self, step=None, index=None, in_run=False):
  
          self.logger = logging.getLogger(uuid.uuid4().hex)
  
          # Don't propagate log messages to "root" handler (we get duplicate
          # messages without this)
          # TODO: this prevents us from being able to capture logs with pytest:
          # we should revisit it
          self.logger.propagate = False
  
          loglevel = self.get('option', 'loglevel')
  
          if loglevel=='DEBUG':
              prefix = '| %(levelname)-7s | %(funcName)-10s | %(lineno)-4s'
          else:
              prefix = '| %(levelname)-7s'
  
          if in_run:
              flow = self.get('option', 'flow')
  
              # Figure out how wide to make step and index fields
              max_step_len = 2
              max_index_len = 2
              for future_step in self.getkeys('flowgraph', flow):
                  max_step_len = max(len(future_step) + 1, max_step_len)
                  for future_index in self.getkeys('flowgraph', flow, future_step):
                      max_index_len = max(len(future_index) + 1, max_index_len)
  
              jobname = self.get('option', 'jobname')
  
              if step is None:
                  step = '-' * max(max_step_len // 4, 1)
              if index is None:
                  index = '-' * max(max_index_len // 4, 1)
  
              run_info = f'%s  | %-{max_step_len}s | %-{max_index_len}s' % (jobname, step, index)
              logformat = ' | '.join([prefix, run_info, '%(message)s'])
          else:
              logformat = ' | '.join([prefix, '%(message)s'])
  
          handler = logging.StreamHandler()
          formatter = logging.Formatter(logformat)
  
          handler.setFormatter(formatter)
  
          # Clear any existing handlers so we don't end up with duplicate messages
          # if repeat calls to _init_logger are made
          if len(self.logger.handlers) > 0:
              self.logger.handlers.clear()
  
          self.logger.addHandler(handler)
          self.logger.setLevel(loglevel)
  
      ###########################################################################
      def _deinit_logger(self):
          self.logger = None
  
      ###########################################################################
      def _get_switches(self, *keypath):
          '''Helper function for parsing switches and metavars for a keypath.'''
          #Switch field fully describes switch format
          switch = self.get(*keypath, field='switch')
  
          if switch is None:
              switches = []
          elif isinstance(switch, list):
              switches = switch
          else:
              switches = [switch]
          switchstrs = []
  
          # parse out switch from metavar
          # TODO: should we validate that metavar matches for each switch?
          for switch in switches:
              switchmatch = re.match(r'(-[\w_]+)\s+(.*)', switch)
              gccmatch = re.match(r'(-[\w_]+)(.*)', switch)
              plusmatch = re.match(r'(\+[\w_\+]+)(.*)', switch)
  
              if switchmatch:
                  switchstr = switchmatch.group(1)
                  metavar = switchmatch.group(2)
              elif gccmatch:
                  switchstr = gccmatch.group(1)
                  metavar = gccmatch.group(2)
              elif plusmatch:
                  switchstr = plusmatch.group(1)
                  metavar = plusmatch.group(2)
              switchstrs.append(switchstr)
  
          return switchstrs, metavar
  
      ###########################################################################
      def create_cmdline(self, progname, description=None, switchlist=None, input_map=None):
          """Creates an SC command line interface.
  
          Exposes parameters in the SC schema as command line switches,
          simplifying creation of SC apps with a restricted set of schema
          parameters exposed at the command line. The order of command
          line switch settings parsed from the command line is as follows:
  
           1. loglevel
           2. fpga_partname
           3. load_target('target')
           4. read_manifest([cfg])
           5. all other switches
  
          The cmdline interface is implemented using the Python argparse package
          and the following use restrictions apply.
  
          * Help is accessed with the '-h' switch.
          * Arguments that include spaces must be enclosed with double quotes.
          * List parameters are entered individually. (ie. -y libdir1 -y libdir2)
          * For parameters with Boolean types, the switch implies "true".
          * Special characters (such as '-') must be enclosed in double quotes.
          * Compiler compatible switches include: -D, -I, -O{0,1,2,3}
          * Verilog legacy switch formats are supported: +libext+, +incdir+
  
          Args:
              progname (str): Name of program to be executed.
              description (str): Short program description.
              switchlist (list of str): List of SC parameter switches to expose
                  at the command line. By default all SC schema switches are
                  available. Parameter switches should be entered based on the
                  parameter 'switch' field in the schema. For parameters with
                  multiple switches, both will be accepted if any one is included
                  in this list.
              input_map (dict of str): Dictionary mapping file extensions to input
                  filetypes. This is used to automatically assign positional
                  source arguments to ['input', ...] keypaths based on their file
                  extension. If None, the CLI will not accept positional source
                  arguments.
  
          Examples:
              >>> chip.create_cmdline(progname='sc-show',switchlist=['-input','-cfg'])
              Creates a command line interface for 'sc-show' app.
  
              >>> chip.create_cmdline(progname='sc', input_map={'v': 'verilog'})
              All sources ending in .v will be stored in ['input', 'verilog']
          """
  
          # Argparse
          parser = argparse.ArgumentParser(prog=progname,
                                           prefix_chars='-+',
                                           formatter_class=argparse.RawDescriptionHelpFormatter,
                                           description=description)
  
          # Get all keys from global dictionary or override at command line
          allkeys = self.getkeys()
  
          # Iterate over all keys to add parser arguments
          for keypath in allkeys:
              #Fetch fields from leaf cell
              helpstr = self.get(*keypath, field='shorthelp')
              typestr = self.get(*keypath, field='type')
  
              # argparse 'dest' must be a string, so join keypath with commas
              dest = '_'.join(keypath)
  
              switchstrs, metavar = self._get_switches(*keypath)
  
              # Three switch types (bool, list, scalar)
              if not switchlist or any(switch in switchlist for switch in switchstrs):
                  if typestr == 'bool':
                      parser.add_argument(*switchstrs,
                                          nargs='?',
                                          metavar=metavar,
                                          dest=dest,
                                          const='true',
                                          help=helpstr,
                                          default=argparse.SUPPRESS)
                  #list type arguments
                  elif re.match(r'\[', typestr):
                      #all the rest
                      parser.add_argument(*switchstrs,
                                          metavar=metavar,
                                          dest=dest,
                                          action='append',
                                          help=helpstr,
                                          default=argparse.SUPPRESS)
                  else:
                      #all the rest
                      parser.add_argument(*switchstrs,
                                          metavar=metavar,
                                          dest=dest,
                                          help=helpstr,
                                          default=argparse.SUPPRESS)
  
          if input_map is not None:
              parser.add_argument('source',
                                  nargs='*',
                                  help='Input files with filetype inferred by extension')
  
          #Preprocess sys.argv to enable linux commandline switch formats
          #(gcc, verilator, etc)
          scargs = []
  
          # Iterate from index 1, otherwise we end up with script name as a
          # 'source' positional argument
          for item in sys.argv[1:]:
              #Split switches with one character and a number after (O0,O1,O2)
              opt = re.match(r'(\-\w)(\d+)', item)
              #Split assign switches (-DCFG_ASIC=1)
              assign = re.search(r'(\-\w)(\w+\=\w+)', item)
              #Split plusargs (+incdir+/path)
              plusarg = re.search(r'(\+\w+\+)(.*)', item)
              if opt:
                  scargs.append(opt.group(1))
                  scargs.append(opt.group(2))
              elif plusarg:
                  scargs.append(plusarg.group(1))
                  scargs.append(plusarg.group(2))
              elif assign:
                  scargs.append(assign.group(1))
                  scargs.append(assign.group(2))
              else:
                  scargs.append(item)
  
          parser.add_argument('-version', action='version', version=_metadata.version)
  
          #Grab argument from pre-process sysargs
          cmdargs = vars(parser.parse_args(scargs))
  
          # Print banner
          print(_metadata.banner)
          print("Authors:", ", ".join(_metadata.authors))
          print("Version:", _metadata.version, "\n")
          print("-"*80)
  
          os.environ["COLUMNS"] = '80'
  
          # 1. set loglevel if set at command line
          if 'option_loglevel' in cmdargs.keys():
              self.logger.setLevel(cmdargs['option_loglevel'])
  
          # 2. read in target if set
          if 'option_target' in cmdargs.keys():
              if 'arg_pdk' in cmdargs.keys():
                  raise NotImplementedError("NOT IMPLEMENTED: ['arg', 'pdk'] parameter with target")
              if 'arg_flow' in cmdargs.keys():
                  raise NotImplementedError("NOT IMPLEMENTED: ['arg', 'flow'] parameter with target")
              if 'fpga_partname' in cmdargs.keys():
                  self.set('fpga', 'partname', cmdargs['fpga_partname'], clobber=True)
              # running target command
              self.load_target(cmdargs['option_target'])
  
          # 4. read in all cfg files
          if 'option_cfg' in cmdargs.keys():
              for item in cmdargs['option_cfg']:
                  self.read_manifest(item, clobber=True, clear=True)
  
          # Map sources to ['input'] keypath.
          if 'source' in cmdargs:
              for source in cmdargs['source']:
                  _, ext = os.path.splitext(source)
                  ext = ext.lstrip('.')
                  if ext in input_map:
                      filetype = input_map[ext]
                      if self.valid('input', filetype, quiet=True):
                          self.add('input', filetype, source)
                      else:
                          self.set('input', filetype, source)
                      self.logger.info(f'Source {source} inferred as {filetype}')
                  else:
                      self.logger.warning('Unable to infer input type for '
                          f'{source} based on file extension, ignoring. Use the '
                          '-input flag to provide it explicitly.')
              # we don't want to handle this in the next loop
              del cmdargs['source']
  
          # 5. Cycle through all command args and write to manifest
          for dest, vals in cmdargs.items():
              keypath = dest.split('_')
  
              # Turn everything into a list for uniformity
              if not isinstance(vals, list):
                  vals = [vals]
  
              # Cycle through all items
              for item in vals:
                  # Hack to handle the fact that we want optmode stored with an 'O'
                  # prefix.
                  if keypath == ['option', 'optmode']:
                      item = 'O' + item
  
                  num_free_keys = keypath.count('default')
  
                  if len(item.split(' ')) < num_free_keys + 1:
                      # Error out if value provided doesn't have enough words to
                      # fill in 'default' keys.
                      switches, metavar = self._get_switches(*keypath)
                      switchstr = '/'.join(switches)
                      self.logger.error(f'Invalid value {item} for switch {switchstr}. Expected format {metavar}.')
                      raise SiliconCompilerError('Invalid CLI arguments')
  
                  # We replace 'default' in keypath with first N words in provided
                  # value. Remainder is the actual value we want to store in the
                  # parameter.
                  *free_keys, val = item.split(' ', num_free_keys)
                  args = [free_keys.pop(0) if key == 'default' else key for key in keypath]
  
                  # Storing in manifest
                  self.logger.info(f"Command line argument entered: {args} Value: {val}")
                  typestr = self.get(*keypath, field='type')
                  if typestr.startswith('['):
                      if self.valid(*args, quiet=True):
                          self.add(*args, val)
                      else:
                          self.set(*args, val, clobber=True)
                  else:
                      self.set(*args, val, clobber=True)
  
      #########################################################################
      def find_function(self, modulename, funcname, moduletype=None):
          '''
          Returns a function attribute from a module on disk.
  
          Searches the SC root directory and the 'scpath' parameter for the
          modulename provided and imports the module if found. If the funcname
          provided is found in the module, a callable function attribute is
          returned, otherwise None is returned.
  
          The function assumes the following directory structure:
  
          * tools/modulename/modulename.py
          * flows/modulename.py
          * pdks/modulname.py
  
          If the moduletype is None, the module paths are search in the
          order: 'targets'->'flows'->'tools'->'pdks'->'libs'->'checklists'):
  
  
          Supported functions include:
  
          * targets (make_docs, setup)
          * pdks (make_docs, setup)
          * flows (make_docs, setup)
          * tools (make_docs, setup, check_version, runtime_options,
            pre_process, post_process)
          * libs (make_docs, setup)
  
          Args:
              modulename (str): Name of module to import.
              funcname (str): Name of the function to find within the module.
              moduletype (str): Type of module (flows, pdks, libs, checklists, targets).
  
          Examples:
              >>> setup_pdk = chip.find_function('freepdk45', 'setup', 'pdks')
              >>> setup_pdk()
              Imports the freepdk45 module and runs the setup_pdk function
  
          '''
  
          # module search path depends on modtype
          if moduletype is None:
              for item in ('targets', 'flows', 'tools', 'pdks', 'libs', 'checklists'):
                  fullpath = self.find_function(modulename, funcname, module_type=item)
                  if fullpath:
                      break
                  self.logger.error(f"Could not find module {modulename}")
                  self.error = 1
                  return None
          elif moduletype in ('targets','flows', 'pdks', 'libs'):
              fullpath = self._find_sc_file(f"{moduletype}/{modulename}.py", missing_ok=True)
          elif moduletype in ('tools', 'checklists'):
              fullpath = self._find_sc_file(f"{moduletype}/{modulename}/{modulename}.py", missing_ok=True)
          else:
              self.logger.error(f"Illegal module type '{moduletype}'.")
              self.error = 1
              return
  
          # try loading module if found
          if fullpath:
              self.logger.debug(f"Loading function '{funcname}' from module '{modulename}'")
  
              try:
                  spec = importlib.util.spec_from_file_location(modulename, fullpath)
                  imported = importlib.util.module_from_spec(spec)
                  spec.loader.exec_module(imported)
  
                  if hasattr(imported, funcname):
                      function = getattr(imported, funcname)
                  else:
                      function = None
                  return function
              except:
                  traceback.print_exc()
                  self.logger.error(f"Module setup failed for '{modulename}'")
                  self.error = 1
  
      ##########################################################################
      def load_target(self, name):
          """
          Loads a target module and runs the setup() function.
  
          The function searches the $SCPATH for targets/<name>.py and runs
          the setup function in that module if found.
  
          Args:
              name (str): Module name
              flow (str): Target flow to
  
          Examples:
              >>> chip.load_target('freepdk45_demo')
              Loads the 'freepdk45_demo' target
  
          """
  
          self.set('option', 'target', name)
  
          func = self.find_function(name, 'setup', 'targets')
          if func is not None:
              func(self)
          else:
              self.logger.error(f'Target module {name} not found in $SCPATH or siliconcompiler/targets/.')
              raise SiliconCompilerError(f'Target module {name} not found $SCPATH or siliconcompiler/targets/.')
  
      ##########################################################################
      def load_pdk(self, name):
          """
          Loads a PDK module and runs the setup() function.
  
          The function searches the $SCPATH for pdks/<name>.py and runs
          the setup function in that module if found.
  
          Args:
              name (str): Module name
  
          Examples:
              >>> chip.load_pdk('freepdk45_pdk')
              Loads the 'freepdk45' pdk
  
          """
  
          func = self.find_function(name, 'setup', 'pdks')
          if func is not None:
              self.logger.info(f"Loading PDK '{name}'")
              self._loaded_modules['pdks'].append(name)
              func(self)
          else:
              self.logger.error(f'PDK module {name} not found in $SCPATH or siliconcompiler/pdks/.')
              raise SiliconCompilerError(f'PDK module {name} not found in $SCPATH or siliconcompiler/pdks/.')
  
      ##########################################################################
      def load_flow(self, name):
          """
          Loads a flow  module and runs the setup() function.
  
          The function searches the $SCPATH for flows/<name>.py and runs
          the setup function in that module if found.
  
          Args:
              name (str): Module name
  
          Examples:
              >>> chip.load_flow('asicflow')
              Loads the 'asicflow' flow
  
          """
  
          func = self.find_function(name, 'setup', 'flows')
          if func is not None:
              self.logger.info(f"Loading flow '{name}'")
              self._loaded_modules['flows'].append(name)
              func(self)
          else:
              self.logger.error(f'Flow module {name} not found in $SCPATH or siliconcompiler/flows/.')
              raise SiliconCompilerError(f'Flow module {name} not found in $SCPATH or siliconcompiler/flows/.')
  
      ##########################################################################
      def load_lib(self, name):
          """
          Loads a library module and runs the setup() function.
  
          The function searches the $SCPATH for libs/<name>.py and runs
          the setup function in that module if found.
  
          Args:
              name (str): Module name
  
          Examples:
              >>> chip.load_lib('nangate45')
              Loads the 'nangate45' library
  
          """
  
          func = self.find_function(name, 'setup', 'libs')
          if func is not None:
              self.logger.info(f"Loading library '{name}'")
              self._loaded_modules['libs'].append(name)
              func(self)
          else:
              self.logger.error(f'Library module {name} not found in $SCPATH or siliconcompiler/libs/.')
              raise SiliconCompilerError(f'Library module {name} not found in $SCPATH or siliconcompiler/libs/.')
  
      ##########################################################################
      def load_checklist(self, name):
          """
          Loads a checklist module and runs the setup() function.
  
          The function searches the $SCPATH for checklist/<name>/<name>.py and runs
          the setup function in that module if found.
  
          Args:
              name (str): Module name
  
          Examples:
              >>> chip.load_checklist('oh_tapeout')
              Loads the 'oh_tapeout' checklist
  
          """
  
          func = self.find_function(name, 'setup', 'checklists')
          if func is not None:
              self.logger.info(f"Loading checklist '{name}'")
              self._loaded_modules['checklists'].append(name)
              func(self)
          else:
              self.logger.error(f'Checklist module {name} not found in $SCPATH or siliconcompiler/checklists/.')
              raise SiliconCompilerError(f'Checklist module {name} not found in $SCPATH or siliconcompiler/checklists/.')
  
  
      ###########################################################################
      def list_metrics(self):
          '''
          Returns a list of all metrics in the schema.
  
          '''
  
          return self.getkeys('metric','default','default')
  
      ###########################################################################
      def help(self, *keypath):
          """
          Returns a schema parameter description.
  
          Args:
              *keypath(str): Keypath to parameter.
  
          Returns:
              A formatted multi-line help paragraph for the parameter provided.
  
          Examples:
              >>> print(chip.help('asic','diearea'))
              Displays help information about the 'asic, diearea' parameter
  
          """
  
          self.logger.debug('Fetching help for %s', keypath)
  
          #Fetch Values
  
          description = self.get(*keypath, field='shorthelp')
          typestr = self.get(*keypath, field='type')
          switchstr = str(self.get(*keypath, field='switch'))
          defstr = str(self.get(*keypath, field='defvalue'))
          requirement = str(self.get(*keypath, field='require'))
          helpstr = self.get(*keypath, field='help')
          example = self.get(*keypath, field='example')
  
          examplestr = ("\nExamples:    " + example[0] + ''.join(
                       ["\n             " + ex for ex in example[1:]]))
  
          #Removing multiple spaces and newlines
          helpstr = helpstr.rstrip()
          helpstr = helpstr.replace("\n", "")
          helpstr = ' '.join(helpstr.split())
  
          for idx, item in enumerate(example):
              example[idx] = ' '.join(item.split())
              example[idx] = example[idx].replace(", ", ",")
  
          #Wrap text
          para = textwrap.TextWrapper(width=60)
          para_list = para.wrap(text=helpstr)
  
          #Full Doc String
          fullstr = ("-"*80 +
                     "\nDescription: " + description +
                     "\nSwitch:      " + switchstr +
                     "\nType:        " + typestr  +
                     "\nRequirement: " + requirement   +
                     "\nDefault:     " + defstr   +
                     examplestr +
                     "\nHelp:        " + para_list[0] + "\n")
          for line in para_list[1:]:
              fullstr = (fullstr +
                         " "*13 + line.lstrip() + "\n")
  
          return fullstr
  
  
      ###########################################################################
      def valid(self, *args, valid_keypaths=None, quiet=True, default_valid=False):
          """
          Checks validity of a keypath.
  
          Checks the validity of a parameter keypath and returns True if the
          keypath is valid and False if invalid.
  
          Args:
              keypath(list str): Variable length schema key list.
              valid_keypaths (list of list): List of valid keypaths as lists. If
                  None, check against all keypaths in the schema.
              quiet (bool): If True, don't display warnings for invalid keypaths.
  
          Returns:
              Boolean indicating validity of keypath.
  
          Examples:
              >>> check = chip.valid('design')
              Returns True.
              >>> check = chip.valid('blah')
              Returns False.
          """
  
          keypathstr = ','.join(args)
          keylist = list(args)
          if default_valid:
              default = 'default'
          else:
              default = None
  
          if valid_keypaths is None:
              valid_keypaths = self.getkeys()
  
          # Look for a full match with default playing wild card
          for valid_keypath in valid_keypaths:
              if len(keylist) != len(valid_keypath):
                  continue
  
              ok = True
              for i in range(len(keylist)):
                  if valid_keypath[i] not in (keylist[i], default):
                      ok = False
                      break
              if ok:
                  return True
  
          # Match not found
          if not quiet:
              self.logger.warning(f"Keypath [{keypathstr}] is not valid")
          return False
  
      ###########################################################################
      def get(self, *keypath, field='value', job=None, cfg=None):
          """
          Returns a schema parameter field.
  
          Returns a schema parameter field based on the keypath provided in the
          ``*keypath``. See the :ref:`Schema Reference Manual<SiliconCompiler
          Schema>` for documentation of all supported keypaths. The returned type
          is consistent with the type field of the parameter. Fetching parameters
          with empty or undefined value files returns None for scalar types and []
          (empty list) for list types.  Accessing a non-existent keypath produces
          a logger error message and raises the Chip object error flag.
  
          Args:
              keypath(list str): Variable length schema key list.
              field(str): Parameter field to fetch.
              job (str): Jobname to use for dictionary access in place of the
                  current active jobname.
              cfg(dict): Alternate dictionary to access in place of the default
                  chip object schema dictionary.
  
          Returns:
              Value found for the keypath and field provided.
  
          Examples:
              >>> foundry = chip.get('pdk', 'foundry')
              Returns the name of the foundry from the PDK.
  
          """
  
          if cfg is None:
              if job is not None:
                  cfg = self.cfg['history'][job]
              else:
                  cfg = self.cfg
  
          keypathstr = ','.join(keypath)
  
          self.logger.debug(f"Reading from [{keypathstr}]. Field = '{field}'")
          return self._search(cfg, keypathstr, *keypath, field=field, mode='get')
  
      ###########################################################################
      def getkeys(self, *keypath, cfg=None, job=None):
          """
          Returns a list of schema dictionary keys.
  
          Searches the schema for the keypath provided and returns a list of
          keys found, excluding the generic 'default' key. Accessing a
          non-existent keypath produces a logger error message and raises the
          Chip object error flag.
  
          Args:
              keypath (list str): Variable length ordered schema key list
              cfg (dict): Alternate dictionary to access in place of self.cfg
              job (str): Jobname to use for dictionary access in place of the
                  current active jobname.
  
          Returns:
              List of keys found for the keypath provided.
  
          Examples:
              >>> keylist = chip.getkeys('pdk')
              Returns all keys for the 'pdk' keypath.
              >>> keylist = chip.getkeys()
              Returns all list of all keypaths in the schema.
          """
  
          if cfg is None:
              if job is None:
                  cfg = self.cfg
              else:
                  cfg = self.cfg['history'][job]
  
          if len(list(keypath)) > 0:
              keypathstr = ','.join(keypath)
              self.logger.debug('Getting schema parameter keys for: %s', keypathstr)
              keys = list(self._search(cfg, keypathstr, *keypath, mode='getkeys'))
              if 'default' in keys:
                  keys.remove('default')
          else:
              self.logger.debug('Getting all schema parameter keys.')
              keys = list(self._allkeys(cfg))
  
          return keys
  
      ###########################################################################
      def getdict(self, *keypath, cfg=None):
          """
          Returns a schema dictionary.
  
          Searches the schema for the keypath provided and returns a complete
          dictionary. Accessing a non-existent keypath produces a logger error
          message and raises the Chip object error flag.
  
          Args:
              keypath(list str): Variable length ordered schema key list
              cfg(dict): Alternate dictionary to access in place of self.cfg
  
          Returns:
              A schema dictionary
  
          Examples:
              >>> pdk = chip.getdict('pdk')
              Returns the complete dictionary found for the keypath 'pdk'
          """
  
          if cfg is None:
              cfg = self.cfg
  
          if len(list(keypath)) > 0:
              keypathstr = ','.join(keypath)
              self.logger.debug('Getting cfg for: %s', keypathstr)
              localcfg = self._search(cfg, keypathstr, *keypath, mode='getcfg')
  
          return copy.deepcopy(localcfg)
  
      ###########################################################################
      def set(self, *args, field='value', clobber=True, cfg=None):
          '''
          Sets a schema parameter field.
  
          Sets a schema parameter field based on the keypath and value provided in
          the ``*args``. See the :ref:`Schema Reference Manual<SiliconCompiler
          Schema>` for documentation of all supported keypaths. New schema
          dictionaries are automatically created for keypaths that overlap with
          'default' dictionaries. The write action is ignored if the parameter
          value is non-empty and the clobber option is set to False.
  
          The value provided must agree with the dictionary parameter 'type'.
          Accessing a non-existent keypath or providing a value that disagrees
          with the parameter type produces a logger error message and raises the
          Chip object error flag.
  
          Args:
              args (list): Parameter keypath followed by a value to set.
              field (str): Parameter field to set.
              clobber (bool): Existing value is overwritten if True.
              cfg(dict): Alternate dictionary to access in place of self.cfg
  
          Examples:
              >>> chip.set('design', 'top')
              Sets the name of the design to 'top'
          '''
  
          if cfg is None:
              cfg = self.cfg
  
          # Verify that all keys are strings
          for key in args[:-1]:
              if not isinstance(key,str):
                  self.logger.error(f"Key [{key}] is not a string [{args}]")
  
          keypathstr = ','.join(args[:-1])
          all_args = list(args)
  
          # Special case to ensure loglevel is updated ASAP
          if len(args) == 3 and args[1] == 'loglevel' and field == 'value':
              self.logger.setLevel(args[2])
  
          self.logger.debug(f"Setting [{keypathstr}] to {args[-1]}")
          return self._search(cfg, keypathstr, *all_args, field=field, mode='set', clobber=clobber)
  
      ###########################################################################
      def add(self, *args, cfg=None, field='value'):
          '''
          Adds item(s) to a schema parameter list.
  
          Adds item(s) to schema parameter list based on the keypath and value
          provided in the ``*args``.  See the :ref:`Schema Reference
          Manual<SiliconCompiler Schema>` for documentation of all supported
          keypaths. New schema dictionaries are automatically created for keypaths
          that overlap with 'default' dictionaries.
  
          The value provided must agree with the dictionary parameter 'type'.
          Accessing a non-existent keypath, providing a value that disagrees
          with the parameter type, or using add with a scalar parameter produces
          a logger error message and raises the Chip object error flag.
  
          Args:
              args (list): Parameter keypath followed by a value to add.
              cfg(dict): Alternate dictionary to access in place of self.cfg
              field (str): Parameter field to set.
  
          Examples:
              >>> chip.add('source', 'hello.v')
              Adds the file 'hello.v' to the list of sources.
          '''
  
          if cfg is None:
              cfg = self.cfg
  
          # Verify that all keys are strings
          for key in args[:-1]:
              if not isinstance(key,str):
                  self.logger.error(f"Key [{key}] is not a string [{args}]")
  
          keypathstr = ','.join(args[:-1])
          all_args = list(args)
  
          self.logger.debug(f'Appending value {args[-1]} to [{keypathstr}]')
          return self._search(cfg, keypathstr, *all_args, field=field, mode='add')
  
  
      ###########################################################################
      def _allkeys(self, cfg, keys=None, keylist=None):
          '''
          Returns list of all keypaths in the schema.
          '''
  
          if keys is None:
              keylist = []
              keys = []
          for k in cfg:
              newkeys = keys.copy()
              newkeys.append(k)
              if 'defvalue' in cfg[k]:
                  keylist.append(newkeys)
              else:
                  self._allkeys(cfg[k], keys=newkeys, keylist=keylist)
          return keylist
  
      ###########################################################################
      def _search(self, cfg, keypath, *args, field='value', mode='get', clobber=True):
          '''
          Internal recursive function that searches the Chip schema for a
          match to the combination of *args and fields supplied. The function is
          used to set and get data within the dictionary.
  
          Args:
              cfg(dict): The cfg schema to search
              keypath (str): Concatenated keypath used for error logging.
              args (str): Keypath/value variable list used for access
              field(str): Leaf cell field to access.
              mode(str): Action (set/get/add/getkeys/getkeys)
              clobber(bool): Specifies to clobber (for set action)
  
          '''
  
          all_args = list(args)
          param = all_args[0]
          val = all_args[-1]
          empty = [None, 'null', [], 'false']
  
          #set/add leaf cell (all_args=(param,val))
          if (mode in ('set', 'add')) & (len(all_args) == 2):
              # clean error if key not found
              if (not param in cfg) & (not 'default' in cfg):
                  self.logger.error(f"Set/Add keypath [{keypath}] does not exist.")
                  self.error = 1
              else:
                  # making an 'instance' of default if not found
                  if (not param in cfg) & ('default' in cfg):
                      cfg[param] = copy.deepcopy(cfg['default'])
                  list_type =bool(re.match(r'\[', cfg[param]['type']))
                  # checking for illegal fields
                  if not field in cfg[param] and (field != 'value'):
                      self.logger.error(f"Field '{field}' for keypath [{keypath}]' is not a valid field.")
                      self.error = 1
                  # check legality of value
                  if field == 'value':
                      (type_ok,type_error) = self._typecheck(cfg[param], param, val)
                      if not type_ok:
                          self.logger.error("%s", type_error)
                          self.error = 1
                  # converting python True/False to lower case string
                  if (field == 'value') and (cfg[param]['type'] == 'bool'):
                      if val == True:
                          val = "true"
                      elif val == False:
                          val = "false"
                  # checking if value has been set
                  # TODO: fix clobber!!
                  selval = cfg[param]['value']
                  # updating values
                  if cfg[param]['lock'] == "true":
                      self.logger.debug("Ignoring {mode}{} to [{keypath}]. Lock bit is set.")
                  elif (mode == 'set'):
                      if (field != 'value') or (selval in empty) or clobber:
                          if field in ('copy', 'lock'):
                              # boolean fields
                              if val is True:
                                  cfg[param][field] = "true"
                              elif val is False:
                                  cfg[param][field] = "false"
                              else:
                                  self.logger.error(f'{field} must be set to boolean.')
                                  self.error = 1
                          elif field in ('hashalgo', 'scope', 'require', 'type', 'unit',
                                         'shorthelp', 'notes', 'switch', 'help'):
                              # awlays string scalars
                              cfg[param][field] = val
                          elif field in ('example'):
                              # list from default schema (already a list)
                              cfg[param][field] = val
                          elif field in ('signature', 'filehash', 'date', 'author'):
                              # convert to list if appropriate
                              if isinstance(val, list) | (not list_type):
                                  cfg[param][field] = val
                              else:
                                  cfg[param][field] = [val]
                          elif (not list_type) & (val is None):
                              # special case for None
                              cfg[param][field] = None
                          elif (not list_type) & (not isinstance(val, list)):
                              # convert to string for scalar value
                              cfg[param][field] = str(val)
                          elif list_type & (not isinstance(val, list)):
                              # convert to string for list value
                              cfg[param][field] = [str(val)]
                          elif list_type & isinstance(val, list):
                              # converting tuples to strings
                              if re.search(r'\(', cfg[param]['type']):
                                  cfg[param][field] = list(map(str,val))
                              else:
                                  cfg[param][field] = val
                          else:
                              self.logger.error(f"Assigning list to scalar for [{keypath}]")
                              self.error = 1
                      else:
                          self.logger.debug(f"Ignoring set() to [{keypath}], value already set. Use clobber=true to override.")
                  elif (mode == 'add'):
                      if field in ('filehash', 'date', 'author', 'signature'):
                          cfg[param][field].append(str(val))
                      elif field in ('copy', 'lock'):
                          self.logger.error(f"Illegal use of add() for scalar field {field}.")
                          self.error = 1
                      elif list_type & (not isinstance(val, list)):
                          cfg[param][field].append(str(val))
                      elif list_type & isinstance(val, list):
                          cfg[param][field].extend(val)
                      else:
                          self.logger.error(f"Illegal use of add() for scalar parameter [{keypath}].")
                          self.error = 1
                  return cfg[param][field]
          #get leaf cell (all_args=param)
          elif len(all_args) == 1:
              if not param in cfg:
                  self.error = 1
                  self.logger.error(f"Get keypath [{keypath}] does not exist.")
              elif mode == 'getcfg':
                  return cfg[param]
              elif mode == 'getkeys':
                  return cfg[param].keys()
              else:
                  if not (field in cfg[param]) and (field!='value'):
                      self.error = 1
                      self.logger.error(f"Field '{field}' not found for keypath [{keypath}]")
                  elif field == 'value':
                      #Select default if no value has been set
                      if field not in cfg[param]:
                          selval = cfg[param]['defvalue']
                      else:
                          selval =  cfg[param]['value']
                      #check for list
                      if bool(re.match(r'\[', cfg[param]['type'])):
                          sctype = re.sub(r'[\[\]]', '', cfg[param]['type'])
                          return_list = []
                          if selval is None:
                              return None
                          for item in selval:
                              if sctype == 'int':
                                  return_list.append(int(item))
                              elif sctype == 'float':
                                  return_list.append(float(item))
                              elif sctype.startswith('(str,'):
                                  if isinstance(item,tuple):
                                      return_list.append(item)
                                  else:
                                      tuplestr = re.sub(r'[\(\)\'\s]','',item)
                                      return_list.append(tuple(tuplestr.split(',')))
                              elif sctype.startswith('(float,'):
                                  if isinstance(item,tuple):
                                      return_list.append(item)
                                  else:
                                      tuplestr = re.sub(r'[\(\)\s]','',item)
                                      return_list.append(tuple(map(float, tuplestr.split(','))))
                              else:
                                  return_list.append(item)
                          return return_list
                      else:
                          if selval is None:
                              # Unset scalar of any type
                              scalar = None
                          elif cfg[param]['type'] == "int":
                              #print(selval, type(selval))
                              scalar = int(float(selval))
                          elif cfg[param]['type'] == "float":
                              scalar = float(selval)
                          elif cfg[param]['type'] == "bool":
                              scalar = (selval == 'true')
                          elif re.match(r'\(', cfg[param]['type']):
                              tuplestr = re.sub(r'[\(\)\s]','',selval)
                              scalar = tuple(map(float, tuplestr.split(',')))
                          else:
                              scalar = selval
                          return scalar
                  #all non-value fields are strings (or lists of strings)
                  else:
                      if cfg[param][field] == 'true':
                          return True
                      elif cfg[param][field] == 'false':
                          return False
                      else:
                          return cfg[param][field]
          #if not leaf cell descend tree
          else:
              ##copying in default tree for dynamic trees
              if not param in cfg and 'default' in cfg:
                  cfg[param] = copy.deepcopy(cfg['default'])
              elif not param in cfg:
                  self.error = 1
                  self.logger.error(f"Get keypath [{keypath}] does not exist.")
                  return None
              all_args.pop(0)
              return self._search(cfg[param], keypath, *all_args, field=field, mode=mode, clobber=clobber)
  
      ###########################################################################
      def _prune(self, cfg, top=True, keeplists=False):
          '''
          Internal recursive function that creates a local copy of the Chip
          schema (cfg) with only essential non-empty parameters retained.
  
          '''
  
          # create a local copy of dict
          if top:
              localcfg = copy.deepcopy(cfg)
          else:
              localcfg = cfg
  
          #10 should be enough for anyone...
          maxdepth = 10
          i = 0
  
          #Prune when the default & value are set to the following
          if keeplists:
              empty = ("null", None)
          else:
              empty = ("null", None, [])
  
          # When at top of tree loop maxdepth times to make sure all stale
          # branches have been removed, not elegant, but stupid-simple
          # "good enough"
          while i < maxdepth:
              #Loop through all keys starting at the top
              for k in list(localcfg.keys()):
                  #removing all default/template keys
                  # reached a default subgraph, delete it
                  if k == 'default':
                      del localcfg[k]
                  # reached leaf-cell
                  elif 'help' in localcfg[k].keys():
                      del localcfg[k]['help']
                  elif 'example' in localcfg[k].keys():
                      del localcfg[k]['example']
                  elif 'defvalue' in localcfg[k].keys():
                      if localcfg[k]['defvalue'] in empty:
                          if 'value' in localcfg[k].keys():
                              if localcfg[k]['value'] in empty:
                                  del localcfg[k]
                          else:
                              del localcfg[k]
                  #removing stale branches
                  elif not localcfg[k]:
                      localcfg.pop(k)
                  #keep traversing tree
                  else:
                      self._prune(cfg=localcfg[k], top=False, keeplists=keeplists)
              if top:
                  i += 1
              else:
                  break
  
          return localcfg
  
      ###########################################################################
      def _find_sc_file(self, filename, missing_ok=False):
          """
          Returns the absolute path for the filename provided.
  
          Searches the SC root directory and the 'scpath' parameter for the
          filename provided and returns the absolute path. If no valid absolute
          path is found during the search, None is returned.
  
          Shell variables ('$' followed by strings consisting of numbers,
          underscores, and digits) are replaced with the variable value.
  
          Args:
              filename (str): Relative or absolute filename.
  
          Returns:
              Returns absolute path of 'filename' if found, otherwise returns
              None.
  
          Examples:
              >>> chip._find_sc_file('flows/asicflow.py')
             Returns the absolute path based on the sc installation directory.
  
          """
  
          # Replacing environment variables
          filename = self._resolve_env_vars(filename)
  
          # If we have a path relative to our cwd or an abs path, pass-through here
          if os.path.exists(os.path.abspath(filename)):
              return os.path.abspath(filename)
  
          # Otherwise, search relative to scpaths
          scpaths = [self.scroot, self.cwd]
          scpaths.extend(self.get('option', 'scpath'))
          if 'SCPATH' in os.environ:
              scpaths.extend(os.environ['SCPATH'].split(os.pathsep))
  
          searchdirs = ', '.join(scpaths)
          self.logger.debug(f"Searching for file {filename} in {searchdirs}")
  
          result = None
          for searchdir in scpaths:
              if not os.path.isabs(searchdir):
                  searchdir = os.path.join(self.cwd, searchdir)
  
              abspath = os.path.abspath(os.path.join(searchdir, filename))
              if os.path.exists(abspath):
                  result = abspath
                  break
  
          if result is None and not missing_ok:
              self.error = 1
              self.logger.error(f"File {filename} was not found")
  
          return result
  
      ###########################################################################
      def find_files(self, *keypath, cfg=None, missing_ok=False, job=None):
          """
          Returns absolute paths to files or directories based on the keypath
          provided.
  
          By default, this function first checks if the keypath provided has its
          `copy` parameter set to True. If so, it returns paths to the files in
          the build directory. Otherwise, it resolves these files based on the
          current working directory and SC path.
  
          The keypath provided must point to a schema parameter of type file, dir,
          or lists of either. Otherwise, it will trigger an error.
  
          Args:
              keypath (list str): Variable length schema key list.
              cfg (dict): Alternate dictionary to access in place of the default
                  chip object schema dictionary.
              missing_ok (bool): If True, silently return None when files aren't
                  found. If False, print an error and set the error flag.
              job (str): Jobname to use for dictionary access in place of the
                  current active jobname.
  
          Returns:
              If keys points to a scalar entry, returns an absolute path to that
              file/directory, or None if not found. It keys points to a list
              entry, returns a list of either the absolute paths or None for each
              entry, depending on whether it is found.
  
          Examples:
              >>> chip.find_files('source')
              Returns a list of absolute paths to source files, as specified in
              the schema.
  
          """
          if cfg is None:
              cfg = self.cfg
  
          copyall = self.get('option', 'copyall', cfg=cfg, job=job)
          paramtype = self.get(*keypath, field='type', cfg=cfg, job=job)
  
          if 'file' in paramtype:
              copy = self.get(*keypath, field='copy', cfg=cfg, job=job)
          else:
              copy = False
  
          if 'file' not in paramtype and 'dir' not in paramtype:
              self.logger.error('Can only call find_files on file or dir types')
              self.error = 1
              return None
  
          is_list = bool(re.match(r'\[', paramtype))
  
          paths = self.get(*keypath, cfg=cfg, job=job)
          # Convert to list if we have scalar
          if not is_list:
              paths = [paths]
  
          result = []
  
          # Special cases for various ['eda', ...] files that may be implicitly
          # under the workdir (or refdir in the case of scripts).
          # TODO: it may be cleaner to have a file resolution scope flag in schema
          # (e.g. 'scpath', 'workdir', 'refdir'), rather than harcoding special
          # cases.
          if keypath[0] == 'tool' and keypath[2] in ('input', 'output', 'report'):
              step = keypath[3]
              index = keypath[4]
              if keypath[2] == 'report':
                  io = ""
              else:
                  io = keypath[2] + 's'
              iodir = os.path.join(self._getworkdir(jobname=job, step=step, index=index), io)
              for path in paths:
                  abspath = os.path.join(iodir, path)
                  if os.path.isfile(abspath):
                      result.append(abspath)
              return result
          elif keypath[0] == 'tool' and keypath[2] == 'script':
              tool = keypath[1]
              step = keypath[3]
              index = keypath[4]
              refdirs = self.find_files('tool', tool, 'refdir', step, index)
              for path in paths:
                  for refdir in refdirs:
                      abspath = os.path.join(refdir, path)
                      if os.path.isfile(abspath):
                          result.append(abspath)
                          break
  
              return result
  
          for path in paths:
              if (copyall or copy) and ('file' in paramtype):
                  name = self._get_imported_filename(path)
                  abspath = os.path.join(self._getworkdir(jobname=job, step='import'), 'outputs', name)
                  if os.path.isfile(abspath):
                      # if copy is True and file is found in import outputs,
                      # continue. Otherwise, fall through to _find_sc_file (the
                      # file may not have been gathered in imports yet)
                      result.append(abspath)
                      continue
              result.append(self._find_sc_file(path, missing_ok=missing_ok))
          # Convert back to scalar if that was original type
          if not is_list:
              return result[0]
  
          return result
  
      ###########################################################################
      def find_result(self, filetype, step, jobname=None, index='0'):
          """
          Returns the absolute path of a compilation result.
  
          Utility function that returns the absolute path to a results
          file based on the provided arguments. The result directory
          structure is:
  
          <dir>/<design>/<jobname>/<step>/<index>/outputs/<design>.filetype
  
          Args:
              filetype (str): File extension (.v, .def, etc)
              step (str): Task step name ('syn', 'place', etc)
              jobname (str): Jobid directory name
              index (str): Task index
  
          Returns:
              Returns absolute path to file.
  
          Examples:
              >>> manifest_filepath = chip.find_result('.vg', 'syn')
             Returns the absolute path to the manifest.
          """
          if jobname is None:
              jobname = self.get('option', 'jobname')
  
          workdir = self._getworkdir(jobname, step, index)
          design = self.get('design')
          filename = f"{workdir}/outputs/{design}.{filetype}"
  
          self.logger.debug("Finding result %s", filename)
  
          if os.path.isfile(filename):
              return filename
          else:
              return None
  
      ###########################################################################
      def _abspath(self, cfg):
          '''
          Internal function that goes through provided dictionary and resolves all
          relative paths where required.
          '''
  
          for keypath in self.getkeys(cfg=cfg):
              paramtype = self.get(*keypath, cfg=cfg, field='type')
              value = self.get(*keypath, cfg=cfg)
              if value:
                  #only do something if type is file or dir
                  if 'file' in paramtype or 'dir' in paramtype:
                      abspaths = self.find_files(*keypath, cfg=cfg, missing_ok=True)
                      self.set(*keypath, abspaths, cfg=cfg)
  
      ###########################################################################
      def _print_csv(self, cfg, fout):
          csvwriter = csv.writer(fout)
          csvwriter.writerow(['Keypath', 'Value'])
  
          allkeys = self.getkeys(cfg=cfg)
          for key in allkeys:
              keypath = ','.join(key)
              value = self.get(*key, cfg=cfg)
              if isinstance(value,list):
                  for item in value:
                      csvwriter.writerow([keypath, item])
              else:
                  csvwriter.writerow([keypath, value])
  
      ###########################################################################
      def _escape_val_tcl(self, val, typestr):
          '''Recursive helper function for converting Python values to safe TCL
          values, based on the SC type string.'''
          if val is None:
              return ''
          elif typestr.startswith('('):
              # Recurse into each item of tuple
              subtypes = typestr.strip('()').split(',')
              valstr = ' '.join(self._escape_val_tcl(v, subtype.strip())
                                for v, subtype in zip(val, subtypes))
              return f'[list {valstr}]'
          elif typestr.startswith('['):
              # Recurse into each item of list
              subtype = typestr.strip('[]')
              valstr = ' '.join(self._escape_val_tcl(v, subtype) for v in val)
              return f'[list {valstr}]'
          elif typestr == 'bool':
              return 'true' if val else 'false'
          elif typestr == 'str':
              # Escape string by surrounding it with "" and escaping the few
              # special characters that still get considered inside "". We don't
              # use {}, since this requires adding permanent backslashes to any
              # curly braces inside the string.
              # Source: https://www.tcl.tk/man/tcl8.4/TclCmd/Tcl.html (section [4] on)
              escaped_val = (val.replace('\\', '\\\\') # escape '\' to avoid backslash substition (do this first, since other replaces insert '\')
                                .replace('[', '\\[')   # escape '[' to avoid command substition
                                .replace('$', '\\$')   # escape '$' to avoid variable substition
                                .replace('"', '\\"'))  # escape '"' to avoid string terminating early
              return '"' + escaped_val + '"'
          elif typestr in ('file', 'dir'):
              # Replace $VAR with $env(VAR) for tcl
              val = re.sub(r'\$(\w+)', r'$env(\1)', val)
              # Same escapes as applied to string, minus $ (since we want to resolve env vars).
              escaped_val = (val.replace('\\', '\\\\') # escape '\' to avoid backslash substition (do this first, since other replaces insert '\')
                                .replace('[', '\\[')   # escape '[' to avoid command substition
                                .replace('"', '\\"'))  # escape '"' to avoid string terminating early
              return '"' +  escaped_val + '"'
          else:
              # floats/ints just become strings
              return str(val)
  
      ###########################################################################
      def _print_tcl(self, cfg, fout=None, prefix=""):
          '''
          Prints out schema as TCL dictionary
          '''
  
          fout.write("#############################################")
          fout.write("#!!!! AUTO-GENERATED FILE. DO NOT EDIT!!!!!!")
          fout.write("#############################################\n")
  
          allkeys = self.getkeys(cfg=cfg)
  
          for key in allkeys:
              typestr = self.get(*key, cfg=cfg, field='type')
              value = self.get(*key, cfg=cfg)
  
              #create a TCL dict
              keystr = ' '.join(key)
  
              valstr = self._escape_val_tcl(value, typestr)
  
              if not (typestr.startswith('[') or typestr.startswith('(')):
                  # treat scalars as lists as well
                  valstr = f'[list {valstr}]'
  
              outstr = f"{prefix} {keystr} {valstr}\n"
  
              #print out all non default values
              if 'default' not in key:
                  fout.write(outstr)
  
      ###########################################################################
  
      def merge_manifest(self, cfg, job=None, clobber=True, clear=True, check=False):
          """
          Merges an external manifest with the current compilation manifest.
  
          All value fields in the provided schema dictionary are merged into the
          current chip object. Dictionaries with non-existent keypath produces a
          logger error message and raises the Chip object error flag.
  
          Args:
              job (str): Specifies non-default job to merge into
              clear (bool): If True, disables append operations for list type
              clobber (bool): If True, overwrites existing parameter value
              check (bool): If True, checks the validity of each key
              partial (bool): If True, perform a partial merge, only merging
                  keypaths that may have been updated during run().
  
          Examples:
              >>> chip.merge_manifest('my.pkg.json')
             Merges all parameters in my.pk.json into the Chip object
  
          """
          self._merge_manifest(cfg, job, clobber, clear, check)
  
      def _key_may_be_updated(self, keypath):
          '''Helper that returns whether `keypath` can be updated mid-run.'''
          # TODO: cleaner way to manage this?
          if keypath[0] in ('metric', 'record'):
              return True
          if keypath[0] == 'flowgraph' and keypath[4] in ('select', 'status'):
              return True
          return False
  
      ###########################################################################
      def _merge_manifest(self, cfg, job=None, clobber=True, clear=True, check=False, partial=False):
          """
          Internal merge_manifest() implementation with `partial` arg.
  
          partial (bool): If True, perform a partial merge, only merging keypaths
          that may have been updated during run().
          """
          if job is not None:
              # fill ith default schema before populating
              self.cfg['history'][job] = schema_cfg()
              dst = self.cfg['history'][job]
          else:
              dst = self.cfg
  
          for keylist in self.getkeys(cfg=cfg):
              if partial and not self._key_may_be_updated(keylist):
                  continue
              if keylist[0] in ('history', 'library'):
                  continue
              #only read in valid keypaths without 'default'
              key_valid = True
              if check:
                  key_valid = self.valid(*keylist, quiet=False, default_valid=True)
              if key_valid and 'default' not in keylist:
                  # update value, handling scalars vs. lists
                  typestr = self.get(*keylist, cfg=cfg, field='type')
                  val = self.get(*keylist, cfg=cfg)
                  arg = keylist.copy()
                  arg.append(val)
                  if bool(re.match(r'\[', typestr)) & bool(not clear):
                      self.add(*arg, cfg=dst)
                  else:
                      self.set(*arg, cfg=dst, clobber=clobber)
  
                  # update other fields that a user might modify
                  for field in self.getdict(*keylist, cfg=cfg).keys():
                      if field in ('value', 'switch', 'type', 'require', 'defvalue',
                                   'shorthelp', 'example', 'help'):
                          # skip these fields (value handled above, others are static)
                          continue
                      v = self.get(*keylist, cfg=cfg, field=field)
                      self.set(*keylist, v, cfg=dst, field=field)
  
      ###########################################################################
      def _keypath_empty(self, key):
          '''
          Utility function to check key for an empty list.
          '''
  
          emptylist = ("null", None, [])
  
          value = self.get(*key)
          defvalue = self.get(*key, field='defvalue')
          value_empty = (defvalue in emptylist) and (value in emptylist)
  
          return value_empty
  
      ###########################################################################
      def _check_files(self):
          allowed_paths = [os.path.join(self.cwd, self.get('option', 'builddir'))]
          allowed_paths.extend(os.environ['SC_VALID_PATHS'].split(os.pathsep))
  
          for keypath in self.getkeys():
              if 'default' in keypath:
                  continue
  
              paramtype = self.get(*keypath, field='type')
              #only do something if type is file or dir
              if ('history' not in keypath and 'library' not in keypath) and ('file' in paramtype or 'dir' in paramtype):
  
                  if self.get(*keypath) is None:
                      # skip unset values (some directories are None by default)
                      continue
  
                  abspaths = self.find_files(*keypath, missing_ok=True)
                  if not isinstance(abspaths, list):
                      abspaths = [abspaths]
  
                  for abspath in abspaths:
                      ok = False
  
                      if abspath is not None:
                          for allowed_path in allowed_paths:
                              if os.path.commonpath([abspath, allowed_path]) == allowed_path:
                                  ok = True
                                  continue
  
                      if not ok:
                          self.logger.error(f'Keypath {keypath} contains path(s) '
                              'that do not exist or resolve to files outside of '
                              'allowed directories.')
                          return False
  
          return True
  
      ###########################################################################
      def check_filepaths(self):
          '''
          Verifies that paths to all files in manifest are valid.
          '''
  
          allkeys = self.getkeys()
          for keypath in allkeys:
              allpaths = []
              paramtype = self.get(*keypath, field='type')
              if 'file' in paramtype or 'dir' in paramtype:
                  if 'dir' not in keypath and self.get(*keypath):
                      allpaths = list(self.get(*keypath))
                  for path in allpaths:
                      #check for env var
                      m = re.match(r'\$(\w+)(.*)', path)
                      if m:
                          prefix_path = os.environ[m.group(1)]
                          path = prefix_path + m.group(2)
                      file_error = 'file' in paramtype and not os.path.isfile(path)
                      dir_error = 'dir' in paramtype and not os.path.isdir(path)
                      if file_error or dir_error:
                          self.logger.error(f"Paramater {keypath} path {path} is invalid")
                          self.error = 1
  
      ###########################################################################
      def _check_manifest_dynamic(self, step, index):
          '''Runtime checks called from _runtask().
  
          - Make sure expected inputs exist.
          - Make sure all required filepaths resolve correctly.
          '''
          flow = self.get('option', 'flow')
          tool = self.get('flowgraph', flow, step, index, 'tool')
          if self.valid('tool', tool, 'input', step, index):
              required_inputs = self.get('tool', tool, 'input', step, index)
          else:
              required_inputs = []
          input_dir = os.path.join(self._getworkdir(step=step, index=index), 'inputs')
          for filename in required_inputs:
              path = os.path.join(input_dir, filename)
              if not os.path.isfile(path):
                  self.logger.error(f'Required input {filename} not received for {step}{index}.')
                  self.error = 1
  
          if (not tool in self.builtin) and self.valid('tool', tool, 'require', step, index):
              all_required = self.get('tool', tool, 'require', step, index)
              for item in all_required:
                  keypath = item.split(',')
                  paramtype = self.get(*keypath, field='type')
                  if ('file' in paramtype) or ('dir' in paramtype):
                      abspath = self.find_files(*keypath)
                      if abspath is None or (isinstance(abspath, list) and None in abspath):
                          self.logger.error(f"Required file keypath {keypath} can't be resolved.")
                          self.error = 1
  
          # Need to run this check here since file resolution can change in
          # _runtask().
          if 'SC_VALID_PATHS' in os.environ:
              if not self._check_files():
                  self.error = 1
  
          return self.error
  
      ###########################################################################
      def check_manifest(self):
          '''
          Verifies the integrity of the pre-run compilation manifest.
  
          Checks the validity of the current schema manifest in
          memory to ensure that the design has been properly set up prior
          to running compilation. The function is called inside the run()
          function but can also be called separately. Checks performed by the
          check_manifest() function include:
  
          * Has a flowgraph been defined?
          * Does the manifest satisfy the schema requirement field settings?
          * Are all flowgraph input names legal step/index pairs?
          * Are the tool parameter setting requirements met?
  
          Returns:
              Returns True if the manifest is valid, else returns False.
  
          Examples:
              >>> manifest_ok = chip.check_manifest()
              Returns True of the Chip object dictionary checks out.
  
          '''
          # Dynamic checks
          # We only perform these if arg, step and arg, index are set.
          # We don't check inputs for skip all
          # TODO: Need to add skip step
  
          cur_step = self.get('arg', 'step')
          cur_index = self.get('arg', 'index')
          if cur_step and cur_index and not self.get('option', 'skipall'):
              return self._check_manifest_dynamic(cur_step, cur_index)
  
          design = self.get('design')
          flow = self.get('option', 'flow')
          jobname = self.get('option', 'jobname')
          steplist = self.get('option', 'steplist')
          if not steplist:
              steplist = self.list_steps()
  
          #1. Checking that flowgraph and steplist are legal
          if flow not in self.getkeys('flowgraph'):
              self.error = 1
              self.logger.error(f"flowgraph {flow} not defined.")
          legal_steps = self.getkeys('flowgraph',flow)
  
          if 'import' not in legal_steps:
              self.error = 1
              self.logger.error("Flowgraph doesn't contain import step.")
  
          indexlist = {}
          #TODO: refactor
          for step in steplist:
              if self.get('option', 'indexlist'):
                  indexlist[step] = self.get('option', 'indexlist')
              else:
                  indexlist[step] = self.getkeys('flowgraph', flow, step)
  
          for step in steplist:
              for index in indexlist[step]:
                  in_job = None
                  if (step in self.getkeys('option', 'jobinput') and
                      index in self.getkeys('option', 'jobinput', step)):
                      in_job = self.get('option', 'jobinput', step, index)
  
                  for in_step, in_index in self.get('flowgraph', flow, step, index, 'input'):
                      if in_job is not None:
                          workdir = self._getworkdir(jobname=in_job, step=in_step, index=in_index)
                          cfg = os.path.join(workdir, 'outputs', f'{design}.pkg.json')
                          if not os.path.isfile(cfg):
                              self.logger.error(f'{step}{index} relies on {in_step}{in_index} from job {in_job}, '
                                  'but this task has not been run.')
                              self.error = 1
                          continue
                      if in_step in steplist and in_index in indexlist[in_step]:
                          # we're gonna run this step, OK
                          continue
                      if self.get('flowgraph', flow, in_step, in_index, 'status') == TaskStatus.SUCCESS:
                          # this task has already completed successfully, OK
                          continue
                      self.logger.error(f'{step}{index} relies on {in_step}{in_index}, '
                          'but this task has not been run and is not in the current steplist.')
                      self.error = 1
  
          #2. Check libary names
          for item in self.get('asic', 'logiclib'):
              if item not in self.getkeys('library'):
                  self.error = 1
                  self.logger.error(f"Target library {item} not found.")
  
          #3. Check requirements list
          allkeys = self.getkeys()
          for key in allkeys:
              keypath = ",".join(key)
              if 'default' not in key and 'history' not in key and 'library' not in key:
                  key_empty = self._keypath_empty(key)
                  requirement = self.get(*key, field='require')
                  if key_empty and (str(requirement) == 'all'):
                      self.error = 1
                      self.logger.error(f"Global requirement missing for [{keypath}].")
                  elif key_empty and (str(requirement) == self.get('option', 'mode')):
                      self.error = 1
                      self.logger.error(f"Mode requirement missing for [{keypath}].")
  
          #4. Check per tool parameter requirements (when tool exists)
          for step in steplist:
              for index in self.getkeys('flowgraph', flow, step):
                  tool = self.get('flowgraph', flow, step, index, 'tool')
                  if (tool not in self.builtin) and (tool in self.getkeys('tool')):
                      # checking that requirements are set
                      if self.valid('tool', tool, 'require', step, index):
                          all_required = self.get('tool', tool, 'require', step, index)
                          for item in all_required:
                              keypath = item.split(',')
                              if self._keypath_empty(keypath):
                                  self.error = 1
                                  self.logger.error(f"Value empty for [{keypath}] for {tool}.")
                      if self._keypath_empty(['tool', tool, 'exe']):
                          self.error = 1
                          self.logger.error(f'Executable not specified for tool {tool}')
  
          if 'SC_VALID_PATHS' in os.environ:
              if not self._check_files():
                  self.error = 1
  
          if not self._check_flowgraph_io():
              self.error = 1
  
          return self.error
  
      ###########################################################################
      def _gather_outputs(self, step, index):
          '''Return set of filenames that are guaranteed to be in outputs
          directory after a successful run of step/index.'''
  
          flow = self.get('option', 'flow')
          tool = self.get('flowgraph', flow, step, index, 'tool')
  
          outputs = set()
          if tool in self.builtin:
              in_tasks = self.get('flowgraph', flow, step, index, 'input')
              in_task_outputs = [self._gather_outputs(*task) for task in in_tasks]
  
              if tool in ('minimum', 'maximum'):
                  if len(in_task_outputs) > 0:
                      outputs = in_task_outputs[0].intersection(*in_task_outputs[1:])
              elif tool in ('join', 'nop'):
                  if len(in_task_outputs) > 0:
                      outputs = in_task_outputs[0].union(*in_task_outputs[1:])
              else:
                  # TODO: logic should be added here when mux/verify builtins are implemented.
                  self.logger.error(f'Builtin {tool} not yet implemented')
          else:
              # Not builtin tool
              if self.valid('tool', tool, 'output', step, index):
                  outputs = set(self.get('tool', tool, 'output', step, index))
              else:
                  outputs = set()
  
          if step == 'import':
              imports = {self._get_imported_filename(p) for p in self._collect_paths()}
              outputs.update(imports)
  
          return outputs
  
      ###########################################################################
      def _check_flowgraph_io(self):
          '''Check if flowgraph is valid in terms of input and output files.
  
          Returns True if valid, False otherwise.
          '''
  
          flow = self.get('option', 'flow')
          steplist = self.get('option', 'steplist')
  
          if not steplist:
              steplist = self.list_steps()
  
          if len(steplist) < 2:
              return True
  
          for step in steplist:
              for index in self.getkeys('flowgraph', flow, step):
                  # For each task, check input requirements.
                  tool = self.get('flowgraph', flow, step, index, 'tool')
                  if tool in self.builtin:
                      # We can skip builtins since they don't have any particular
                      # input requirements -- they just pass through what they
                      # receive.
                      continue
  
                  # Get files we receive from input tasks.
                  in_tasks = self.get('flowgraph', flow, step, index, 'input')
                  if len(in_tasks) > 1:
                      self.logger.error(f'Tool task {step}{index} has more than one input task.')
                  elif len(in_tasks) > 0:
                      in_step, in_index = in_tasks[0]
                      if in_step not in steplist:
                          # If we're not running the input step, the required
                          # inputs need to already be copied into the build
                          # directory.
                          jobname = self.get('option', 'jobname')
                          if self.valid('option', 'jobinput', step, index):
                              in_job = self.get('option', 'jobinput', step, index)
                          else:
                              in_job = jobname
                          workdir = self._getworkdir(jobname=in_job, step=in_step, index=in_index)
                          in_step_out_dir = os.path.join(workdir, 'outputs')
                          inputs = set(os.listdir(in_step_out_dir))
                      else:
                          inputs = self._gather_outputs(in_step, in_index)
                  else:
                      inputs = set()
  
                  if self.valid('tool', tool, 'input', step, index):
                      requirements = self.get('tool', tool, 'input', step, index)
                  else:
                      requirements = []
                  for requirement in requirements:
                      if requirement not in inputs:
                          self.logger.error(f'Invalid flow: {step}{index} will '
                              f'not receive required input {requirement}.')
                          return False
  
          return True
  
      ###########################################################################
      def read_manifest(self, filename, job=None, clear=True, clobber=True):
          """
          Reads a manifest from disk and merges it with the current compilation manifest.
  
          The file format read is determined by the filename suffix. Currently
          json (*.json) and yaml(*.yaml) formats are supported.
  
          Args:
              filename (filepath): Path to a manifest file to be loaded.
              job (str): Specifies non-default job to merge into.
              clear (bool): If True, disables append operations for list type.
              clobber (bool): If True, overwrites existing parameter value.
  
          Examples:
              >>> chip.read_manifest('mychip.json')
              Loads the file mychip.json into the current Chip object.
          """
          self._read_manifest(filename, job=job, clear=clear, clobber=clobber)
  
      ###########################################################################
      def _read_manifest(self, filename, job=None, clear=True, clobber=True, partial=False):
          """
          Internal read_manifest() implementation with `partial` arg.
  
          partial (bool): If True, perform a partial merge, only merging keypaths
          that may have been updated during run().
          """
  
          filepath = os.path.abspath(filename)
          self.logger.debug(f"Reading manifest {filepath}")
          if not os.path.isfile(filepath):
              error_message =  f"Manifest file not found {filepath}"
              self.logger.error(error_message)
              raise SiliconCompilerError(error_message)
  
          #Read arguments from file based on file type
  
          if filepath.endswith('.gz'):
              fin = gzip.open(filepath, 'r')
          else:
              fin = open(filepath, 'r')
  
          try:
              if re.search(r'(\.json|\.sup)(\.gz)*$', filepath):
                  localcfg = json.load(fin)
              elif re.search(r'(\.yaml|\.yml)(\.gz)*$', filepath):
                  localcfg = yaml.load(fin, Loader=yaml.SafeLoader)
              else:
                  self.logger.error('File format not recognized %s', filepath)
                  self.error = 1
          finally:
              fin.close()
  
          if self.get('schemaversion') != localcfg['schemaversion']['value']:
              self.logger.warning('Attempting to read manifest with incompatible '
              'schema version into current chip object. Skipping...')
              return
  
          # Merging arguments with the Chip configuration
          self._merge_manifest(localcfg, job=job, clear=clear, clobber=clobber, partial=partial)
  
          # Read history
          if 'history' in localcfg and not partial:
              for historic_job in localcfg['history'].keys():
                  self._merge_manifest(localcfg['history'][historic_job],
                                          job=historic_job,
                                          clear=clear,
                                          clobber=clobber,
                                          partial=False)
  
          if 'library' in localcfg and not partial:
              for libname in localcfg['library'].keys():
                  if libname in self.cfg['library']:
                      # TODO: should we make this a proper merge?
                      self.logger.warning(f'Overwriting existing library {libname} '
                          f'in object with values read from {filename}.')
                  self._import_library(libname, localcfg['library'][libname])
  
      ###########################################################################
      def write_manifest(self, filename, prune=True, abspath=False, job=None):
          '''
          Writes the compilation manifest to a file.
  
          The write file format is determined by the filename suffix. Currently
          json (*.json), yaml (*.yaml), tcl (*.tcl), and (*.csv) formats are
          supported.
  
          Args:
              filename (filepath): Output filepath
              prune (bool): If True, essential non-empty parameters from the
                   the Chip object schema are written to the output file.
              abspath (bool): If set to True, then all schema filepaths
                   are resolved to absolute filepaths.
  
          Examples:
              >>> chip.write_manifest('mydump.json')
              Prunes and dumps the current chip manifest into mydump.json
          '''
  
          filepath = os.path.abspath(filename)
          self.logger.debug('Writing manifest to %s', filepath)
  
          if not os.path.exists(os.path.dirname(filepath)):
              os.makedirs(os.path.dirname(filepath))
  
          if prune:
              self.logger.debug('Pruning dictionary before writing file %s', filepath)
              # Keep empty lists to simplify TCL coding
              if filepath.endswith('.tcl'):
                  keeplists = True
              else:
                  keeplists = False
              cfgcopy = self._prune(self.cfg, keeplists=keeplists)
          else:
              cfgcopy = copy.deepcopy(self.cfg)
  
          # resolve absolute paths
          if abspath:
              self._abspath(cfgcopy)
  
          is_csv = re.search(r'(\.csv)(\.gz)*$', filepath)
  
          # format specific dumping
          if filepath.endswith('.gz'):
              fout = gzip.open(filepath, 'wt', encoding='UTF-8')
          elif is_csv:
              # Files written using csv library should be opened with newline=''
              # https://docs.python.org/3/library/csv.html#id3
              fout = open(filepath, 'w', newline='')
          else:
              fout = open(filepath, 'w')
  
          # format specific printing
          try:
              if re.search(r'(\.json|\.sup)(\.gz)*$', filepath):
                  fout.write(json.dumps(cfgcopy, indent=4, sort_keys=True))
              elif re.search(r'(\.yaml|\.yml)(\.gz)*$', filepath):
                  fout.write(yaml.dump(cfgcopy, Dumper=YamlIndentDumper, default_flow_style=False))
              elif re.search(r'(\.tcl)(\.gz)*$', filepath):
                  self._print_tcl(cfgcopy, prefix="dict set sc_cfg", fout=fout)
              elif is_csv:
                  self._print_csv(cfgcopy, fout=fout)
              else:
                  self.logger.error('File format not recognized %s', filepath)
                  self.error = 1
          finally:
              fout.close()
  
      ###########################################################################
      def check_checklist(self, standard, items=None, check_ok=False):
          '''
          Check items in a checklist.
  
          Checks the status of items in a checklist for the standard provided. If
          a specific list of items is unspecified, all items are checked.
  
          All items have an associated 'task' parameter, which indicates which
          tasks can be used to automatically validate the item. For an item to be
          checked, all tasks must satisfy the item's criteria, unless waivers are
          provided. In addition, that task must have generated EDA report files
          for each metric in the criteria.
  
          For items without an associated task, the only requirement is that at
          least one report has been added to that item.
  
          When 'check_ok' is True, every item must also have its 'ok' parameter
          set to True, indicating that a human has reviewed the item.
  
          Args:
              standard (str): Standard to check.
              items (list of str): Items to check from standard.
              check_ok (bool): Whether to check item 'ok' parameter.
  
          Returns:
              Status of item check.
  
          Examples:
              >>> status = chip.check_checklist('iso9000', 'd000')
              Returns status.
          '''
  
          self.logger.info(f'Checking checklist {standard}')
  
          if items is None:
              items = self.getkeys('checklist', standard)
  
          flow = self.get('option', 'flow')
  
          for item in items:
              all_criteria = self.get('checklist', standard, item, 'criteria')
              for criteria in all_criteria:
                  m = re.match(r'(\w+)([\>\=\<]+)(\w+)', criteria)
                  if not m:
                      self.logger.error(f"Illegal checklist criteria: {criteria}")
                      self.error = 1
                      return False
                  elif m.group(1) not in self.getkeys('metric', 'default', 'default'):
                      self.logger.error(f"Critera must use legal metrics only: {criteria}")
                      self.error = 1
                      return False
  
                  metric = m.group(1)
                  op = m.group(2)
                  goal = float(m.group(3))
  
                  tasks = self.get('checklist', standard, item, 'task')
                  for job, step, index in tasks:
                      # Automated checks
                      flow = self.get('option', 'flow', job=job)
                      tool = self.get('flowgraph', flow, step, index, 'tool', job=job)
  
                      value = self.get('metric', step, index, metric,  job=job)
                      criteria_ok = self._safecompare(value, op, goal)
                      if metric in self.getkeys('checklist', standard, item, 'waiver'):
                          waivers = self.get('checklist', standard, item, 'waiver', metric)
                      else:
                          waivers = []
  
                      criteria_str = f'{metric}{op}{goal}'
                      if not criteria_ok and waivers:
                          self.logger.warning(f'{item} criteria {criteria_str} unmet by task {step}{index}, but found waivers.')
                      elif not criteria_ok:
                          self.logger.error(f'{item} criteria {criteria_str} unmet by task {step}{index}.')
                          self.error = 1
                          return False
  
                      if (step in self.getkeys('tool', tool, 'report', job=job) and
                          index in self.getkeys('tool', tool, 'report', step, job=job) and
                          metric in self.getkeys('tool', tool, 'report', step, index, job=job)):
                          eda_reports = self.find_files('tool', tool, 'report', step, index, metric, job=job)
                      else:
                          eda_reports = None
  
                      if not eda_reports:
                          self.logger.error(f'No EDA reports generated for metric {metric} in task {step}{index}')
                          self.error = 1
                          return False
  
                      for report in eda_reports:
                          if report not in self.get('checklist', standard, item, 'report'):
                              self.add('checklist', standard, item, 'report', report)
  
              if len(self.get('checklist', standard, item, 'report')) == 0:
                  # TODO: validate that report exists?
                  self.logger.error(f'No report documenting item {item}')
                  self.error = 1
                  return False
  
              if check_ok and not self.get('checklist', standard, item, 'ok'):
                  self.logger.error(f"Item {item} 'ok' field not checked")
                  self.error = 1
                  return False
  
          self.logger.info('Check succeeded!')
  
          return True
  
      ###########################################################################
      def read_file(self, filename, step='import', index='0'):
          '''
          Read file defined in schema. (WIP)
          '''
          return(0)
  
      ###########################################################################
      def update(self):
          '''
          Update the chip dependency graph.
  
          1. Finds all packages in the local cache
          2. Fetches all packages in the remote registry
          3. Creates a dependency graph based on current chip dependencies and
             dependencies read from dependency json objects.
          4. If autoinstall is set, copy registry packages to local cache.
          5. Error out if package is not found in local cache or in registry.
          6. Error out if autoinstall is set and registry package is missing.
  
          '''
  
          # schema settings
          design = self.get('design')
          reglist = self.get('option', 'registry')
          auto = self.get('option','autoinstall')
  
          # environment settings
          # Local cache location
          if 'SC_HOME' in os.environ:
              home = os.environ['SC_HOME']
          else:
              home = os.environ['HOME']
  
          cache = os.path.join(home,'.sc','registry')
  
          # Indexing all local cache packages
          local = self._build_index(cache)
          remote = self._build_index(reglist)
  
          # Cycle through current chip dependencies
          deps = {}
          for dep in self.getkeys('package', 'dependency'):
              deps[dep] = self.get('package', 'dependency', dep)
  
          depgraph = self._find_deps(cache, local, remote, design, deps, auto)
  
          # Update dependency graph
          for dep in depgraph:
              self.set('package', 'depgraph', dep, depgraph[dep])
  
          return depgraph
  
      ###########################################################################
      def _build_index(self, dirlist):
          '''
          Build a package index for a registry.
          '''
  
          if not isinstance(dirlist, list):
              dirlist = [dirlist]
  
          index = {}
          for item in dirlist:
              if re.match(r'http', item):
                  #TODO
                  pass
              else:
                  packages = os.listdir(item)
                  for i in packages:
                      versions = os.listdir(os.path.join(item, i))
                      index[i] = {}
                      for j in versions:
                          index[i][j] = item
  
          return index
  
      ###########################################################################
      def _install_package(self, cache, dep, ver, remote):
          '''
          Copies a package from remote to local.
          The remote and local arguments are package indices of format:
          index['dirname']['dep']
          '''
  
          package = f"{dep}-{ver}.sup.gz"
  
          self.logger.info(f"Installing package {package} in {cache}")
  
          # Check that package exists in remote registry
          if dep in remote.keys():
              if ver not in list(remote[dep].keys()):
                  self.logger.error(f"Package {dep}-{ver} not found in registry.")
                  sys.exit()
  
          ifile = os.path.join(remote[dep][ver],dep,ver,package)
          odir = os.path.join(cache,dep,ver)
          ofile = os.path.join(odir,package)
  
          # Install package
          os.makedirs(odir, exist_ok=True)
          shutil.copyfile(ifile, ofile)
  
      ###########################################################################
      def _find_deps(self, cache, local, remote, design, deps, auto, depgraph={}, upstream={}):
          '''
          Recursive function to find and install dependencies.
          '''
  
          # install missing dependencies
          depgraph[design] = []
          for dep in deps.keys():
              #TODO: Proper PEP semver matching
              ver = list(deps[dep])[0]
              depgraph[design].append((dep,ver))
              islocal = False
              if dep in local.keys():
                  if ver in local[dep]:
                      islocal = True
  
              # install and update local index
              if auto and islocal:
                  self.logger.info(f"Found package {dep}-{ver} in cache")
              elif auto and not islocal:
                  self._install_package(cache, dep, ver, remote)
                  local[dep]=ver
  
              # look through dependency package files
              package = os.path.join(cache,dep,ver,f"{dep}-{ver}.sup.gz")
              if not os.path.isfile(package):
                  self.logger.error("Package missing. Try 'autoinstall' or install manually.")
                  sys.exit()
              with gzip.open(package, 'r') as f:
                  localcfg = json.load(f)
  
              # done if no more dependencies
              if 'dependency' in localcfg['package']:
                  subdeps = {}
                  subdesign = localcfg['design']['value']
                  depgraph[subdesign] = []
                  for item in localcfg['package']['dependency'].keys():
                      subver = localcfg['package']['dependency'][item]['value']
                      if (item in upstream) and (upstream[item] == subver):
                          # Circular imports are not supported.
                          raise SiliconCompilerError(f'Cannot process circular import: {dep}-{ver} <---> {item}-{subver}.')
                      subdeps[item] = subver
                      upstream[item] = subver
                      depgraph[subdesign].append((item, subver))
                      self._find_deps(cache, local, remote, subdesign, subdeps, auto, depgraph, upstream)
  
          return depgraph
  
      ###########################################################################
      def import_library(self, lib_chip):
          '''Import a Chip object into current Chip as a library.
  
          Args:
              lib_chip (Chip): An instance of Chip to import.
          '''
          self._import_library(lib_chip.design, lib_chip.cfg)
  
      ###########################################################################
      def _import_library(self, libname, libcfg):
          '''Helper to import library with config 'libconfig' as a library
          'libname' in current Chip object.'''
          self.cfg['library'][libname] = copy.deepcopy(libcfg)
          if 'pdk' in self.cfg['library'][libname]:
              del self.cfg['library'][libname]['pdk']
  
      ###########################################################################
      def write_depgraph(self, filename):
          '''
          Writes the package dependency tree to disk.
  
          Supported graphical render formats include png, svg, gif, pdf and a
          few others. (see https://graphviz.org for more information).
  
          Supported text formats include .md, .rst. (see the Linux 'tree'
          command for more information).
  
          '''
  
          return(0)
  
      ###########################################################################
  
      def write_flowgraph(self, filename, flow=None,
                          fillcolor='#ffffff', fontcolor='#000000',
                          fontsize='14', border=True, landscape=False):
          '''Renders and saves the compilation flowgraph to a file.
  
          The chip object flowgraph is traversed to create a graphviz (\*.dot)
          file comprised of node, edges, and labels. The dot file is a
          graphical representation of the flowgraph useful for validating the
          correctness of the execution flow graph. The dot file is then
          converted to the appropriate picture or drawing format based on the
          filename suffix provided. Supported output render formats include
          png, svg, gif, pdf and a few others. For more information about the
          graphviz project, see see https://graphviz.org/
  
          Args:
              filename (filepath): Output filepath
              flow (str): Name of flowgraph to render
              fillcolor(str): Node fill RGB color hex value
              fontcolor (str): Node font RGB color hex value
              fontsize (str): Node text font size
              border (bool): Enables node border if True
              landscape (bool): Renders graph in landscape layout if True
  
          Examples:
              >>> chip.write_flowgraph('mydump.png')
              Renders the object flowgraph and writes the result to a png file.
          '''
          filepath = os.path.abspath(filename)
          self.logger.debug('Writing flowgraph to file %s', filepath)
          fileroot, ext = os.path.splitext(filepath)
          fileformat = ext.replace(".", "")
  
          if flow is None:
              flow = self.get('option', 'flow')
  
          # controlling border width
          if border:
              penwidth = '1'
          else:
              penwidth = '0'
  
          # controlling graph direction
          if landscape:
              rankdir = 'LR'
          else:
              rankdir = 'TB'
  
          dot = graphviz.Digraph(format=fileformat)
          dot.graph_attr['rankdir'] = rankdir
          dot.attr(bgcolor='transparent')
          for step in self.getkeys('flowgraph',flow):
              irange = 0
              for index in self.getkeys('flowgraph', flow, step):
                  irange = irange +1
              for i in range(irange):
                  index = str(i)
                  node = step+index
                  # create step node
                  tool =  self.get('flowgraph', flow, step, index, 'tool')
                  if tool in self.builtin:
                      labelname = step
                  elif tool is not None:
                      labelname = f"{step}{index}\n({tool})"
                  else:
                      labelname = f"{step}{index}"
                  dot.node(node, label=labelname, bordercolor=fontcolor, style='filled',
                           fontcolor=fontcolor, fontsize=fontsize, ordering="in",
                           penwidth=penwidth, fillcolor=fillcolor)
                  # get inputs
                  all_inputs = []
                  for in_step, in_index in self.get('flowgraph', flow, step, index, 'input'):
                      all_inputs.append(in_step + in_index)
                  for item in all_inputs:
                      dot.edge(item, node)
          dot.render(filename=fileroot, cleanup=True)
  
      ########################################################################
      def _collect_paths(self):
          '''
          Returns list of paths to files that will be collected by import step.
  
          See docstring for _collect() for more details.
          '''
          paths = []
  
          copyall = self.get('option', 'copyall')
          allkeys = self.getkeys()
          for key in allkeys:
              leaftype = self.get(*key, field='type')
              if re.search('file', leaftype):
                  copy = self.get(*key, field='copy')
                  value = self.get(*key)
                  if copyall or copy:
                      for item in value:
                          paths.append(item)
  
          return paths
  
      ########################################################################
      def _collect(self, step, index):
          '''
          Collects files found in the configuration dictionary and places
          them in inputs/. The function only copies in files that have the 'copy'
          field set as true. If 'copyall' is set to true, then all files are
          copied in.
  
          1. indexing like in run, job1
          2. chdir package
          3. run tool to collect files, pickle file in output/design.v
          4. copy in rest of the files below
          5. record files read in to schema
  
          '''
  
          indir = 'inputs'
          flow = self.get('option', 'flow')
  
          if not os.path.exists(indir):
              os.makedirs(indir)
  
          self.logger.info('Collecting input sources')
  
          for path in self._collect_paths():
              filename = self._get_imported_filename(path)
              abspath = self._find_sc_file(path)
              if abspath:
                  self.logger.info(f"Copying {abspath} to '{indir}' directory")
                  shutil.copy(abspath, os.path.join(indir, filename))
              else:
                  self._haltstep(step, index)
  
          outdir = 'outputs'
          if not os.path.exists(outdir):
              os.makedirs(outdir)
  
          # Logic to make links from outputs/ to inputs/, skipping anything that
          # will be output by the tool as well as the manifest. We put this here
          # so that tools used for the import stage don't have to duplicate this
          # logic. We skip this logic for 'join'-based single-step imports, since
          # 'join' does the copy for us.
          tool = self.get('flowgraph', flow, step, index, 'tool')
          if tool not in self.builtin:
              if self.valid('tool', tool, 'output', step, index):
                  outputs = self.get('tool', tool, 'output', step, index)
              else:
                  outputs = []
              design = self.get('design')
              ignore = outputs + [f'{design}.pkg.json']
              utils.copytree(indir, outdir, dirs_exist_ok=True, link=True, ignore=ignore)
          elif tool not in ('join', 'nop'):
              self.error = 1
              self.logger.error(f'Invalid import step builtin {tool}. Must be tool or join.')
  
      ###########################################################################
      def archive(self, step=None, index=None, all_files=False):
          '''Archive a job directory.
  
          Creates a single compressed archive (.tgz) based on the design,
          jobname, and flowgraph in the current chip manifest. Individual
          steps and/or indices can be archived based on argumnets specified.
          By default, all steps and indices in the flowgraph are archived.
          By default, only the outputs directory content and the log file
          are archived.
  
          Args:
              step(str): Step to archive.
              index (str): Index to archive
              all_files (bool): If True, all files are archived.
  
          '''
  
          design = self.get('design')
          jobname = self.get('option', 'jobname')
          buildpath = self.get('option', 'builddir')
          flow = self.get('option', 'flow')
  
          if step:
              steplist = [step]
          elif self.get('arg', 'step'):
              steplist = [self.get('arg', 'step')]
          elif self.get('option', 'steplist'):
              steplist = self.get('option', 'steplist')
          else:
              steplist = self.list_steps()
  
          if step:
              archive_name = f"{design}_{jobname}_{step}.tgz"
          else:
              archive_name = f"{design}_{jobname}.tgz"
  
          with tarfile.open(archive_name, "w:gz") as tar:
              for step in steplist:
                  if index:
                      indexlist = [index]
                  else:
                      indexlist = self.getkeys('flowgraph', flow, step)
                  for item in indexlist:
                      basedir = os.path.join(buildpath, design, jobname, step, item)
                      if all_files:
                           tar.add(os.path.abspath(basedir), arcname=basedir)
                      else:
                          outdir = os.path.join(basedir,'outputs')
                          logfile = os.path.join(basedir, step+'.log')
                          tar.add(os.path.abspath(outdir), arcname=outdir)
                          if os.path.isfile(logfile):
                              tar.add(os.path.abspath(logfile), arcname=logfile)
  
          return archive_name
  
      ###########################################################################
      def hash_files(self, *keypath, algo='sha256', update=True):
          '''Generates hash values for a list of parameter files.
  
          Generates a a hash value for each file found in the keypath.
          If the  update variable is True, the has values are recorded in the
          'filehash' field of the parameter, following the order dictated by
          the files within the 'values' parameter field.
  
          Files are located using the find_files() function.
  
          The file hash calculation is performed basd on the 'algo' setting.
          Supported algorithms include SHA1, SHA224, SHA256, SHA384, SHA512,
          and MD5.
  
          Args:
              *keypath(str): Keypath to parameter.
              algo (str): Algorithm to use for file hash calculation
              update (bool): If True, the hash values are recorded in the
                  chip object manifest.
  
          Returns:
              A list of hash values.
  
          Examples:
              >>> hashlist = hash_files('sources')
               Hashlist gets list of hash values computed from 'sources' files.
          '''
  
          keypathstr = ','.join(keypath)
          #TODO: Insert into find_files?
          if 'file' not in self.get(*keypath, field='type'):
              self.logger.error(f"Illegal attempt to hash non-file parameter [{keypathstr}].")
              self.error = 1
          else:
              filelist = self.find_files(*keypath)
              #cycle through all paths
              hashlist = []
              if filelist:
                  self.logger.info(f'Computing hash value for [{keypathstr}]')
              for filename in filelist:
                  if os.path.isfile(filename):
                      #TODO: Implement algo selection
                      hashobj = hashlib.sha256()
                      with open(filename, "rb") as f:
                          for byte_block in iter(lambda: f.read(4096), b""):
                              hashobj.update(byte_block)
                      hash_value = hashobj.hexdigest()
                      hashlist.append(hash_value)
                  else:
                      self.error = 1
                      self.logger.info(f"Internal hashing error, file not found")
              # compare previous hash to new hash
              oldhash = self.get(*keypath,field='filehash')
              for i,item in enumerate(oldhash):
                  if item != hashlist[i]:
                      self.logger.error(f"Hash mismatch for [{keypath}]")
                      self.error = 1
              self.set(*keypath, hashlist, field='filehash', clobber=True)
  
  
      ###########################################################################
      def audit_manifest(self):
          '''Verifies the integrity of the post-run compilation manifest.
  
          Checks the integrity of the chip object implementation flow after
          the run() function has been completed. Errors, warnings, and debug
          messages are reported through the logger object.
  
          Audit checks performed include:
  
          * Time stamps
          * File modifications
          * Error and warning policy
          * IP and design origin
          * User access
          * License terms
          * Version checks
  
          Returns:
              Returns True if the manifest has integrity, else returns False.
  
          Example:
              >>> chip.audit_manifest()
              Audits the Chip object manifest and returns 0 if successful.
  
          '''
  
          return 0
  
  
      ###########################################################################
      def calc_area(self):
          '''Calculates the area of a rectilinear diearea.
  
          Uses the shoelace formulate to calculate the design area using
          the (x,y) point tuples from the 'diearea' parameter. If only diearea
          paramater only contains two points, then the first and second point
          must be the lower left and upper right points of the rectangle.
          (Ref: https://en.wikipedia.org/wiki/Shoelace_formula)
  
          Returns:
              Design area (float).
  
          Examples:
              >>> area = chip.calc_area()
  
          '''
  
          vertices = self.get('asic', 'diearea')
  
          if len(vertices) == 2:
              width = vertices[1][0] - vertices[0][0]
              height = vertices[1][1] - vertices[0][1]
              area = width * height
          else:
              area = 0.0
              for i in range(len(vertices)):
                  j = (i + 1) % len(vertices)
                  area += vertices[i][0] * vertices[j][1]
                  area -= vertices[j][0] * vertices[i][1]
              area = abs(area) / 2
  
          return area
  
      ###########################################################################
      def calc_yield(self, model='poisson'):
          '''Calculates raw die yield.
  
          Calculates the raw yield of the design as a function of design area
          and d0 defect density. Calculation can be done based on the poisson
          model (default) or the murphy model. The die area and the d0
          parameters are taken from the chip dictionary.
  
          * Poisson model: dy = exp(-area * d0/100).
          * Murphy model: dy = ((1-exp(-area * d0/100))/(area * d0/100))^2.
  
          Args:
              model (string): Model to use for calculation (poisson or murphy)
  
          Returns:
              Design yield percentage (float).
  
          Examples:
              >>> yield = chip.calc_yield()
              Yield variable gets yield value based on the chip manifest.
          '''
  
          d0 = self.get('pdk', 'd0')
          diearea = self.calc_area()
  
          if model == 'poisson':
              dy = math.exp(-diearea * d0/100)
          elif model == 'murphy':
              dy = ((1-math.exp(-diearea * d0/100))/(diearea * d0/100))**2
  
          return dy
  
      ##########################################################################
      def calc_dpw(self):
          '''Calculates dies per wafer.
  
          Calculates the gross dies per wafer based on the design area, wafersize,
          wafer edge margin, and scribe lines. The calculation is done by starting
          at the center of the wafer and placing as many complete design
          footprints as possible within a legal placement area.
  
          Returns:
              Number of gross dies per wafer (int).
  
          Examples:
              >>> dpw = chip.calc_dpw()
              Variable dpw gets gross dies per wafer value based on the chip manifest.
          '''
  
          #PDK information
          wafersize = self.get('pdk', 'wafersize')
          edgemargin = self.get('pdk', 'edgemargin')
          hscribe = self.get('pdk', 'hscribe')
          vscribe = self.get('pdk', 'vscribe')
  
          #Design parameters
          diesize = self.get('asic', 'diesize').split()
          diewidth = (diesize[2] - diesize[0])/1000
          dieheight = (diesize[3] - diesize[1])/1000
  
          #Derived parameters
          radius = wafersize/2 -edgemargin
          stepwidth = (diewidth + hscribe)
          stepheight = (dieheight + vscribe)
  
          #Raster dies out from center until you touch edge margin
          #Work quadrant by quadrant
          dies = 0
          for quad in ('q1', 'q2', 'q3', 'q4'):
              x = 0
              y = 0
              if quad == "q1":
                  xincr = stepwidth
                  yincr = stepheight
              elif quad == "q2":
                  xincr = -stepwidth
                  yincr = stepheight
              elif quad == "q3":
                  xincr = -stepwidth
                  yincr = -stepheight
              elif quad == "q4":
                  xincr = stepwidth
                  yincr = -stepheight
              #loop through all y values from center
              while math.hypot(0, y) < radius:
                  y = y + yincr
                  while math.hypot(x, y) < radius:
                      x = x + xincr
                      dies = dies + 1
                  x = 0
  
          return int(dies)
  
      ###########################################################################
      def grep(self, args, line):
          """
          Emulates the Unix grep command on a string.
  
          Emulates the behavior of the Unix grep command that is etched into
          our muscle memory. Partially implemented, not all features supported.
          The function returns None if no match is found.
  
          Args:
              arg (string): Command line arguments for grep command
              line (string): Line to process
  
          Returns:
              Result of grep command (string).
  
          """
  
          # Quick return if input is None
          if line is None:
              return None
  
          # Partial list of supported grep options
          options = {
              '-v' : False, # Invert the sense of matching
              '-i' : False, # Ignore case distinctions in patterns and data
              '-E' : False, # Interpret PATTERNS as extended regular expressions.
              '-e' : False, # Safe interpretation of pattern starting with "-"
              '-x' : False, # Select only matches that exactly match the whole line.
              '-o' : False, # Print only the match parts of a matching line
              '-w' : False} # Select only lines containing matches that form whole words.
  
          # Split into repeating switches and everything else
          match = re.match(r'\s*((?:\-\w\s)*)(.*)', args)
  
          pattern = match.group(2)
  
          # Split space separated switch string into list
          switches = match.group(1).strip().split(' ')
  
          # Find special -e switch update the pattern
          for i in range(len(switches)):
              if switches[i] == "-e":
                  if i != (len(switches)):
                      pattern = ' '.join(switches[i+1:]) + " " + pattern
                      switches = switches[0:i+1]
                      break
                  options["-e"] = True
              elif switches[i] in options.keys():
                  options[switches[i]] = True
              elif switches[i] !='':
                  print("ERROR",switches[i])
  
          #REGEX
          #TODO: add all the other optinos
          match = re.search(rf"({pattern})", line)
          if bool(match) == bool(options["-v"]):
              return None
          else:
              return line
  
      ###########################################################################
      def check_logfile(self, jobname=None, step=None, index='0',
                        logfile=None, display=True):
          '''
          Checks logfile for patterns found in the 'regex' parameter.
  
          Reads the content of the tasks's log file and compares the content found
          in the task's 'regex' parameter. The matches are stored in the file
          '<design>.<suffix>' in the current directory. The matches are printed to
          STDOUT if display is set to True.
  
          Args:
              jobname (str): Jobid directory name
              step (str): Task step name ('syn', 'place', etc)
              index (str): Task index
              logfile (str): Path to logfile. If None, {step}.log is used.
              display (bool): If True, printes matches to STDOUT.
  
          Returns:
              Dictionary mapping suffixes to number of matches for that suffix's
              regex.
  
          Examples:
              >>> chip.check_logfile('place')
              Searches for regex matches in the place logfile.
          '''
  
          # Using manifest to get defaults
  
          flow = self.get('option', 'flow')
  
          if jobname is None:
              jobname = self.get('option', 'jobname')
          if logfile is None:
              logfile = f"{step}.log"
          if step is None:
              step = self.get('arg', 'step')
          if index is None:
              index = self.getkeys('flowgraph', flow, step)[0]
  
          tool = self.get('flowgraph', flow, step, index, 'tool')
  
          # Creating local dictionary (for speed)
          # self.get is slow
          checks = {}
          matches = {}
          regex_list = []
          if self.valid('tool', tool, 'regex', step, index, 'default'):
              regex_list = self.getkeys('tool', tool, 'regex', step, index)
          for suffix in regex_list:
              checks[suffix] = {}
              checks[suffix]['report'] = open(f"{step}.{suffix}", "w")
              checks[suffix]['args'] = self.get('tool', tool, 'regex', step, index, suffix)
              matches[suffix] = 0
  
          # Looping through patterns for each line
          with open(logfile) as f:
            for line in f:
                for suffix in checks:
                    string = line
                    for item in checks[suffix]['args']:
                        if string is None:
                            break
                        else:
                            string = self.grep(item, string)
                    if string is not None:
                        matches[suffix] += 1
                        #always print to file
                        print(string.strip(), file=checks[suffix]['report'])
                        #selectively print to display
                        if display:
                            self.logger.info(string.strip())
  
          return matches
  
      ###########################################################################
      def _find_leaves(self, steplist):
          '''Helper to find final (leaf) tasks for a given steplist.'''
          flow = self.get('option', 'flow')
  
          # First, iterate over the tasks to generate a set of non-leaf tasks.
          all_tasks = set()
          non_leaf_tasks = set()
          for step in steplist:
              for index in self.getkeys('flowgraph', flow, step):
                  all_tasks.add((step, index))
                  for in_step, in_index in self.get('flowgraph', flow, step, index, 'input'):
                      if in_step in steplist:
                          non_leaf_tasks.add((in_step, in_index))
  
          # Then, find all leaf tasks by elimination.
          return all_tasks.difference(non_leaf_tasks)
  
      ###########################################################################
      def summary(self, steplist=None, show_all_indices=False):
          '''
          Prints a summary of the compilation manifest.
  
          Metrics from the flowgraph steps, or steplist parameter if
          defined, are printed out on a per step basis. All metrics from the
          metric dictionary with weights set in the flowgraph dictionary are
          printed out.
  
          Args:
              show_all_indices (bool): If True, displays metrics for all indices
                  of each step. If False, displays metrics only for winning
                  indices.
  
          Examples:
              >>> chip.summary()
              Prints out a summary of the run to stdout.
          '''
  
          # display whole flowgraph if no steplist specified
          flow = self.get('option', 'flow')
          if not steplist:
              if self.get('option', 'steplist'):
                  steplist = self.get('option', 'steplist')
              else:
                  steplist = self.list_steps()
  
          # Find all tasks that are part of a "winning" path.
          selected_tasks = set()
          to_search = []
  
          # Start search with any successful leaf tasks.
          leaf_tasks = self._find_leaves(steplist)
          for task in leaf_tasks:
              if self.get('flowgraph', flow, *task, 'status') == TaskStatus.SUCCESS:
                  selected_tasks.add(task)
                  to_search.append(task)
  
          # Search backwards, saving anything that was selected by leaf tasks.
          while len(to_search) > 0:
              task = to_search.pop(-1)
              for selected in self.get('flowgraph', flow, *task, 'select'):
                  if selected not in selected_tasks:
                      selected_tasks.add(selected)
                      to_search.append(selected)
  
          # only report tool based steps functions
          for step in steplist.copy():
              if self.get('flowgraph',flow, step,'0','tool') in self.builtin:
                  index = steplist.index(step)
                  del steplist[index]
  
          # job directory
          jobdir = self._getworkdir()
  
          # Custom reporting modes
          paramlist = []
          for item in self.getkeys('option', 'param'):
              paramlist.append(item+"="+self.get('option', 'param', item))
  
          if paramlist:
              paramstr = ', '.join(paramlist)
          else:
              paramstr = "None"
  
          info_list = ["SUMMARY:\n",
                       "design : " + self.get('design'),
                       "params : " + paramstr,
                       "jobdir : "+ jobdir,
                       ]
  
          if self.get('option', 'mode') == 'asic':
              pdk = self.get('option', 'pdk')
              info_list.extend(["foundry : " + self.get('pdk', pdk, 'foundry'),
                                "process : " + pdk,
                                "targetlibs : "+" ".join(self.get('asic', 'logiclib'))])
          elif self.get('option', 'mode') == 'fpga':
              info_list.extend(["partname : "+self.get('fpga','partname')])
  
          info = '\n'.join(info_list)
  
  
          print("-"*135)
          print(info, "\n")
  
          # Stepping through all steps/indices and printing out metrics
          data = []
  
          #Creating Header
          header = []
          indices_to_show = {}
          colwidth = 8
          for step in steplist:
              if show_all_indices:
                  indices_to_show[step] = self.getkeys('flowgraph', flow, step)
              else:
                  indices_to_show[step] = []
                  for index in self.getkeys('flowgraph', flow, step):
                      if (step, index) in selected_tasks:
                          indices_to_show[step].append(index)
  
          # header for data frame
          for step in steplist:
              for index in indices_to_show[step]:
                  header.append(f'{step}{index}'.center(colwidth))
  
          # figure out which metrics have non-zero weights
          metric_list = []
          for step in steplist:
              for metric in self.getkeys('metric','default','default'):
                  if metric in self.getkeys('flowgraph', flow, step, '0', 'weight'):
                      if self.get('flowgraph', flow, step, '0', 'weight', metric) is not None:
                          if metric not in metric_list:
                              metric_list.append(metric)
  
          # print out all metrics
          metrics = []
          for metric in metric_list:
              metrics.append(" " + metric)
              row = []
              for step in steplist:
                  for index in indices_to_show[step]:
                      value = self.get('metric', step, index, metric)
  
                      if value is None:
                          value = 'ERR'
                      else:
                          value = str(value)
  
                      row.append(" " + value.center(colwidth))
              data.append(row)
  
          pandas.set_option('display.max_rows', 500)
          pandas.set_option('display.max_columns', 500)
          pandas.set_option('display.width', 100)
          df = pandas.DataFrame(data, metrics, header)
          print(df.to_string())
          print("-"*135)
  
          # Create a report for the Chip object which can be viewed in a web browser.
          # Place report files in the build's root directory.
          web_dir = os.path.join(self.get('option', 'builddir'),
                                 self.get('design'),
                                 self.get('option', 'jobname'))
          if os.path.isdir(web_dir):
              # Gather essential variables.
              templ_dir = os.path.join(self.scroot, 'templates', 'report')
              design = self.get('design')
              flow = self.get('option', 'flow')
              flow_steps = steplist
              flow_tasks = {}
              for step in flow_steps:
                  flow_tasks[step] = self.getkeys('flowgraph', flow, step)
  
              # Call 'show()' to generate a low-res PNG of the design.
              results_gds = self.find_result('gds', step='export')
              img_data = None
              if results_gds and not self.get('option', 'nodisplay'):
                  self.show(results_gds,
                              ['-rd', 'screenshot=1', '-rd', 'scr_w=1024', '-rd', 'scr_h=1024', '-z'])
                  result_file = os.path.join(web_dir, f'{design}.png')
                  # Result might not exist if there is no display
                  if os.path.isfile(result_file):
                      with open(result_file, 'rb') as img_file:
                          img_data = base64.b64encode(img_file.read()).decode('utf-8')
  
              # Generate results page by passing the Chip manifest into the Jinja2 template.
              env = Environment(loader=FileSystemLoader(templ_dir))
              results_page = os.path.join(web_dir, 'report.html')
              pruned_cfg = self._prune(self.cfg)
              if 'history' in pruned_cfg:
                  del pruned_cfg['history']
              if 'library' in pruned_cfg:
                  del pruned_cfg['library']
              with open(results_page, 'w') as wf:
                  wf.write(env.get_template('sc_report.j2').render(
                      manifest = self.cfg,
                      pruned_cfg = pruned_cfg,
                      metric_keys = metric_list,
                      metrics = self.cfg['metric'],
                      tasks = flow_tasks,
                      img_data = img_data,
                  ))
  
              # Try to open the results and layout only if '-nodisplay' is not set.
              if not self.get('option', 'nodisplay'):
                  try:
                      webbrowser.get(results_page)
                  except webbrowser.Error:
                      # Python 'webbrowser' module includes a limited number of popular defaults.
                      # Depending on the platform, the user may have defined their own with $BROWSER.
                      if 'BROWSER' in os.environ:
                          subprocess.run([os.environ['BROWSER'], results_page])
                      else:
                          self.logger.warning('Unable to open results page in web browser:\n' +
                                              os.path.abspath(os.path.join(web_dir, "report.html")))
  
      ###########################################################################
      def list_steps(self, flow=None):
          '''
          Returns an ordered list of flowgraph steps.
  
          All step keys from the flowgraph dictionary are collected and the
          distance from the root node (ie. without any inputs defined) is
          measured for each step. The step list is then sorted based on
          the distance from root and returned.
  
          Returns:
              A list of steps sorted by distance from the root node.
  
          Example:
              >>> steplist = chip.list_steps()
              Variable steplist gets list of steps sorted by distance from root.
          '''
  
          if flow is None:
              flow = self.get('option', 'flow')
  
          #Get length of paths from step to root
          depth = {}
          for step in self.getkeys('flowgraph', flow):
              depth[step] = 0
              for path in self._allpaths(self.cfg, flow, step, str(0)):
                  if len(list(path)) > depth[step]:
                      depth[step] = len(path)
  
          #Sort steps based on path lenghts
          sorted_dict = dict(sorted(depth.items(), key=lambda depth: depth[1]))
          return list(sorted_dict.keys())
  
      ###########################################################################
      def _allpaths(self, cfg, flow, step, index, path=None):
          '''Recursive helper for finding all paths from provided step, index to
          root node(s) with no inputs.
  
          Returns a list of lists.
          '''
  
          if path is None:
              path = []
  
          inputs = self.get('flowgraph', flow, step, index, 'input', cfg=cfg)
  
          if not self.get('flowgraph', flow, step, index, 'input', cfg=cfg):
              return [path]
          else:
              allpaths = []
              for in_step, in_index in inputs:
                  newpath = path.copy()
                  newpath.append(in_step + in_index)
                  allpaths.extend(self._allpaths(cfg, flow, in_step, in_index, path=newpath))
  
          return allpaths
  
      ###########################################################################
      def clock(self, pin, period, jitter=0):
          """
          Clock configuration helper function.
  
          A utility function for setting all parameters associated with a
          single clock definition in the schema.
  
          The method modifies the following schema parameters:
  
          ['datasheet', name, 'pin']
          ['datasheet', name, 'period']
          ['datasheet', name, 'jitter']
  
          Args:
              pin (str): Full hiearchical path to clk pin.
              period (float): Clock period specified in ns.
              jitter (float): Clock jitter specified in ns.
  
          Examples:
              >>> chip.clock('clk, period=1.0)
             Create a clock named 'clk' with a 1.0ns period.
          """
          design = self.get('design')
          self.set('datasheet', design, 'pin', pin, 'type', 'global', 'clk')
  
          period_range = (period * 1e-9, period * 1e-9, period * 1e-9)
          self.set('datasheet', design, 'pin', pin, 'tperiod', 'global', period_range)
  
          jitter_range = (jitter * 1e-9, jitter * 1e-9, jitter * 1e-9)
          self.set('datasheet', design, 'pin', pin, 'tjitter', 'global', jitter_range)
  
      ###########################################################################
      def node(self, flow, step, tool, index=0):
          '''
          Creates a flowgraph node.
  
          Creates a flowgraph node by binding a tool to a task. A task is defined
          as the combination of a step and index. A tool can be an external
          exeuctable or one of the built in functions in the SiliconCompiler
          framework). Built in functions include: minimum, maximum, join, mux,
          verify.
  
          The method modifies the following schema parameters:
  
          ['flowgraph', flow, step, index, 'tool', tool]
          ['flowgraph', flow, step, index, 'weight', metric]
  
          Args:
              flow (str): Flow name
              step (str): Task step name
              tool (str): Tool (or builtin function) to associate with task.
              index (int): Task index
  
          Examples:
              >>> chip.node('asicflow', 'place', 'openroad', index=0)
              Creates a task with step='place' and index=0 and binds it to the 'openroad' tool.
          '''
  
          # bind tool to node
          self.set('flowgraph', flow, step, str(index), 'tool', tool)
          # set default weights
          for metric in self.getkeys('metric', 'default', 'default'):
              self.set('flowgraph', flow, step, str(index), 'weight', metric, 0)
  
      ###########################################################################
      def edge(self, flow, tail, head, tail_index=0, head_index=0):
          '''
          Creates a directed edge from a tail node to a head node.
  
          Connects the output of a tail node with the input of a head node by
          setting the 'input' field of the head node in the schema flowgraph.
  
          The method modifies the following parameters:
  
          ['flowgraph', flow, head, str(head_index), 'input']
  
          Args:
              flow (str): Name of flow
              tail (str): Name of tail node
              head (str): Name of head node
              tail_index (int): Index of tail node to connect
              head_index (int): Index of head node to connect
  
          Examples:
              >>> chip.edge('place', 'cts')
              Creates a directed edge from place to cts.
          '''
  
          # Handling connecting edges between graphs
          # Not completely name space safe, but feels like this limitation
          # is a non-issue
  
          module_tail = f"{tail}.export"
          module_head = f"{head}.import"
          if module_tail in self.getkeys('flowgraph',flow):
              tail = module_tail
          if module_head in self.getkeys('flowgraph',flow):
              head = module_head
          #TODO: add error checking
          # Adding
          self.add('flowgraph', flow, head, str(head_index), 'input', (tail, str(tail_index)))
  
      ###########################################################################
      def graph(self, flow, subflow, name=None):
          '''
          Instantiates a named flow as a graph in the current flowgraph.
  
          Args:
              flow (str): Name of current flow.
              subflow (str): Name of flow to instantiate
              name (str): Name of instance
  
          Examples:
              >>> chip.graph('asicflow')
              Instantiates Creates a directed edge from place to cts.
          '''
  
          if flow not in self.getkeys('flowgraph'):
              self.cfg['flowgraph'][flow] ={}
  
          # uniquify each step
          for step in self.getkeys('flowgraph',subflow):
              if name is None:
                  newstep = step
              else:
                  newstep = name + "." + step
              if newstep not in self.getkeys('flowgraph', flow):
                  self.cfg['flowgraph'][flow][newstep] ={}
              # recursive copy
              for key in self._allkeys(self.cfg['flowgraph'][subflow][step]):
                  self._copyparam(self.cfg['flowgraph'][subflow][step],
                                  self.cfg['flowgraph'][flow][newstep],
                                  key)
              # update step names
              for index in self.getkeys('flowgraph', flow, newstep):
                  all_inputs = self.get('flowgraph', flow, newstep, index,'input')
                  self.set('flowgraph', flow, newstep, index,'input',[])
                  for in_step, in_index in all_inputs:
                      newin = name + "." + in_step
                      self.add('flowgraph', flow, newstep, index,'input',(newin,in_index))
  
  
      ###########################################################################
      def pipe(self, flow, plan):
          '''
          Creates a pipeline based on an order list of key values pairs.
          '''
  
          for item in plan:
              step = list(item.keys())[0]
              tool = list(item.values())[0]
              self.node(flow, step, tool)
              if step != 'import':
                  self.edge(flow, prevstep, step)
              prevstep = step
  
      ###########################################################################
      def join(self, *tasks):
          '''
          Merges outputs from a list of input tasks.
  
          Args:
              tasks(list): List of input tasks specified as (step,index) tuples.
  
          Returns:
              Input list
  
          Examples:
              >>> select = chip.join([('lvs','0'), ('drc','0')])
             Select gets the list [('lvs','0'), ('drc','0')]
          '''
  
          tasklist = list(tasks)
          sel_inputs = tasklist
  
          # no score for join, so just return 0
          return sel_inputs
  
      ###########################################################################
      def nop(self, *task):
          '''
          A no-operation that passes inputs to outputs.
  
          Args:
              task(list): Input task specified as a (step,index) tuple.
  
          Returns:
              Input task
  
          Examples:
              >>> select = chip.nop(('lvs','0'))
             Select gets the tuple [('lvs',0')]
          '''
  
          return list(task)
  
      ###########################################################################
      def minimum(self, *tasks):
          '''
          Selects the task with the minimum metric score from a list of inputs.
  
          Sequence of operation:
  
          1. Check list of input tasks to see if all metrics meets goals
          2. Check list of input tasks to find global min/max for each metric
          3. Select MIN value if all metrics are met.
          4. Normalize the min value as sel = (val - MIN) / (MAX - MIN)
          5. Return normalized value and task name
  
          Meeting metric goals takes precedence over compute metric scores.
          Only goals with values set and metrics with weights set are considered
          in the calculation.
  
          Args:
              tasks(list): List of input tasks specified as (step,index) tuples.
  
          Returns:
              tuple containing
  
              - score (float): Minimum score
              - task (tuple): Task with minimum score
  
          Examples:
              >>> (score, task) = chip.minimum([('place','0'),('place','1')])
  
          '''
          return self._minmax(*tasks, op="minimum")
  
      ###########################################################################
      def maximum(self, *tasks):
          '''
          Selects the task with the maximum metric score from a list of inputs.
  
          Sequence of operation:
  
          1. Check list of input tasks to see if all metrics meets goals
          2. Check list of input tasks to find global min/max for each metric
          3. Select MAX value if all metrics are met.
          4. Normalize the min value as sel = (val - MIN) / (MAX - MIN)
          5. Return normalized value and task name
  
          Meeting metric goals takes precedence over compute metric scores.
          Only goals with values set and metrics with weights set are considered
          in the calculation.
  
          Args:
              tasks(list): List of input tasks specified as (step,index) tuples.
  
          Returns:
              tuple containing
  
              - score (float): Maximum score.
              - task (tuple): Task with minimum score
  
          Examples:
              >>> (score, task) = chip.maximum([('place','0'),('place','1')])
  
          '''
          return self._minmax(*tasks, op="maximum")
  
      ###########################################################################
      def _minmax(self, *steps, op="minimum", **selector):
          '''
          Shared function used for min and max calculation.
          '''
  
          if op not in ('minimum', 'maximum'):
              raise ValueError('Invalid op')
  
          flow = self.get('option', 'flow')
          steplist = list(steps)
  
          # Keeping track of the steps/indexes that have goals met
          failed = {}
          for step, index in steplist:
              if step not in failed:
                  failed[step] = {}
              failed[step][index] = False
  
              if self.get('flowgraph', flow, step, index, 'status') == TaskStatus.ERROR:
                  failed[step][index] = True
              else:
                  for metric in self.getkeys('metric', step, index):
                      if self.valid('flowgraph', flow, step, index, 'goal', metric):
                          goal = self.get('flowgraph', flow, step, index, 'goal', metric)
                          real = self.get('metric', step, index, metric)
                          if abs(real) > goal:
                              self.logger.warning(f"Step {step}{index} failed "
                                  f"because it didn't meet goals for '{metric}' "
                                  "metric.")
                              failed[step][index] = True
  
          # Calculate max/min values for each metric
          max_val = {}
          min_val = {}
          for metric in self.getkeys('flowgraph', flow, step, '0', 'weight'):
              max_val[metric] = 0
              min_val[metric] = float("inf")
              for step, index in steplist:
                  if not failed[step][index]:
                      real = self.get('metric', step, index, metric)
                      max_val[metric] = max(max_val[metric], real)
                      min_val[metric] = min(min_val[metric], real)
  
          # Select the minimum index
          best_score = float('inf') if op == 'minimum' else float('-inf')
          winner = None
          for step, index in steplist:
              if failed[step][index]:
                  continue
  
              score = 0.0
              for metric in self.getkeys('flowgraph', flow, step, index, 'weight'):
                  weight = self.get('flowgraph', flow, step, index, 'weight', metric)
                  if not weight:
                      # skip if weight is 0 or None
                      continue
  
                  real = self.get('metric', step, index, metric)
  
                  if not (max_val[metric] - min_val[metric]) == 0:
                      scaled = (real - min_val[metric]) / (max_val[metric] - min_val[metric])
                  else:
                      scaled = max_val[metric]
                  score = score + scaled * weight
  
              if ((op == 'minimum' and score < best_score) or
                  (op == 'maximum' and score > best_score)):
                  best_score = score
                  winner = (step,index)
  
          return (best_score, winner)
  
      ###########################################################################
      def verify(self, *tasks, **assertion):
          '''
          Tests an assertion on a list of input tasks.
  
          The provided steplist is verified to ensure that all assertions
          are True. If any of the assertions fail, False is returned.
          Assertions are passed in as kwargs, with the key being a metric
          and the value being a number and an optional conditional operator.
          The allowed conditional operators are: >, <, >=, <=
  
          Args:
              *steps (str): List of steps to verify
              **assertion (str='str'): Assertion to check on metric
  
          Returns:
              True if all assertions hold True for all steps.
  
          Example:
              >>> pass = chip.verify(['drc','lvs'], errors=0)
              Pass is True if the error metrics in the drc, lvs steps is 0.
          '''
          #TODO: implement
          return True
  
      ###########################################################################
      def mux(self, *tasks, **selector):
          '''
          Selects a task from a list of inputs.
  
          The selector criteria provided is used to create a custom function
          for selecting the best step/index pair from the inputs. Metrics and
          weights are passed in and used to select the step/index based on
          the minimum or maximum score depending on the 'op' argument.
  
          The function can be used to bypass the flows weight functions for
          the purpose of conditional flow execution and verification.
  
          Args:
              *steps (str): List of steps to verify
              **selector: Key value selection criteria.
  
          Returns:
              True if all assertions hold True for all steps.
  
          Example:
              >>> sel_stepindex = chip.mux(['route'], wirelength=0)
              Selects the routing stepindex with the shortest wirelength.
          '''
  
          #TODO: modify the _minmax function to feed in alternate weight path
          return None
  
      ###########################################################################
      def _runtask(self, step, index, status):
          '''
          Private per step run method called by run().
  
          The method takes in a step string and index string to indicated what
          to run.
  
          Execution flow:
          T1. Start wall timer
          T2. Defer job to compute node if using job scheduler
          T3. Set up working directory + chdir
          T4. Merge manifests from all input dependancies
          T5. Write manifest to input directory for convenience
          T6. Reset all metrics to 0 (consider removing)
          T7. Select inputs
          T8. Copy data from previous step outputs into inputs
          T9. Check manifest
          T10. Run pre_process() function
          T11. Set environment variables
          T12. Check EXE version
          T13. Save manifest as TCL/YAML
          T14. Start CPU timer
          T15. Run EXE
          T16. stop CPU timer
          T17. Run post_process()
          T18. Check log file
          T19. Hash all task files
          T20. Stop Wall timer
          T21. Make a task record
          T22. Save manifest to disk
          T23. Halt if any errors found
          T24. Clean up
          T25. chdir
  
          Note that since _runtask occurs in its own process with a separate
          address space, any changes made to the `self` object will not
          be reflected in the parent. We rely on reading/writing the chip manifest
          to the filesystem to communicate updates between processes.
          '''
  
          self._init_logger(step, index, in_run=True)
  
          ##################
          # Shared parameters (long function!)
          design = self.get('design')
          flow = self.get('option', 'flow')
          tool = self.get('flowgraph', flow, step, index, 'tool')
          quiet = self.get('option', 'quiet') and (step not in self.get('option', 'bkpt'))
  
          ##################
          # 1. Start wall timer
          wall_start = time.time()
  
          ##################
          # 2. Defer job to compute node
          # If the job is configured to run on a cluster, collect the schema
          # and send it to a compute node for deferred execution.
          # (Run the initial 'import' stage[s] locally)
  
          if self.get('option', 'jobscheduler') and \
             self.get('flowgraph', flow, step, index, 'input'):
              # Note: The _deferstep method blocks until the compute node
              # finishes processing this step, and it sets the active/error bits.
              _deferstep(self, step, index, status)
              return
  
          ##################
          # 3. Directory setup
          # support for sharing data across jobs
          job = self.get('option', 'jobname')
          in_job = job
          if step in self.getkeys('option', 'jobinput'):
              if index in self.getkeys('option', 'jobinput', step):
                  in_job = self.get('option', 'jobinput', step, index)
  
          workdir = self._getworkdir(step=step,index=index)
          cwd = os.getcwd()
          if os.path.isdir(workdir):
              shutil.rmtree(workdir)
          os.makedirs(workdir, exist_ok=True)
  
          os.chdir(workdir)
          os.makedirs('outputs', exist_ok=True)
          os.makedirs('reports', exist_ok=True)
  
          ##################
          # 4. Merge manifests from all input dependancies
  
          all_inputs = []
          if not self.get('option', 'remote'):
              for in_step, in_index in self.get('flowgraph', flow, step, index, 'input'):
                  in_task_status = status[in_step + in_index]
                  self.set('flowgraph', flow, in_step, in_index, 'status', in_task_status)
                  if in_task_status != TaskStatus.ERROR:
                      cfgfile = f"../../../{in_job}/{in_step}/{in_index}/outputs/{design}.pkg.json"
                      self._read_manifest(cfgfile, clobber=False, partial=True)
  
          ##################
          # 5. Write manifest prior to step running into inputs
  
          self.set('arg', 'step', None, clobber=True)
          self.set('arg', 'index', None, clobber=True)
          os.makedirs('inputs', exist_ok=True)
          #self.write_manifest(f'inputs/{design}.pkg.json')
  
          ##################
          # 6. Make metrics zero
          # TODO: There should be no need for this, but need to fix
          # without it we need to be more careful with flows to make sure
          # things like the builtin functions don't look at None values
          for metric in self.getkeys('metric', 'default', 'default'):
              self.set('metric', step, index, metric, 0)
  
          ##################
          # 7. Select inputs
  
          args = self.get('flowgraph', flow, step, index, 'args')
          inputs = self.get('flowgraph', flow, step, index, 'input')
  
          sel_inputs = []
          score = 0
  
          if tool in self.builtin:
              self.logger.info(f"Running built in task '{tool}'")
              # Figure out which inputs to select
              if tool == 'minimum':
                  (score, sel_inputs) = self.minimum(*inputs)
              elif tool == "maximum":
                  (score, sel_inputs) = self.maximum(*inputs)
              elif tool == "mux":
                  (score, sel_inputs) = self.mux(*inputs, selector=args)
              elif tool == "join":
                  sel_inputs = self.join(*inputs)
              elif tool == "verify":
                  if not self.verify(*inputs, assertion=args):
                      self._haltstep(step, index)
          else:
              sel_inputs = self.get('flowgraph', flow, step, index, 'input')
  
          if sel_inputs == None:
              self.logger.error(f'No inputs selected after running {tool}')
              self._haltstep(step, index)
  
          self.set('flowgraph', flow, step, index, 'select', sel_inputs)
  
          ##################
          # 8. Copy (link) output data from previous steps
  
          if step == 'import':
              self._collect(step, index)
  
          if not self.get('flowgraph', flow, step, index,'input'):
              all_inputs = []
          elif not self.get('flowgraph', flow, step, index, 'select'):
              all_inputs = self.get('flowgraph', flow, step, index,'input')
          else:
              all_inputs = self.get('flowgraph', flow, step, index, 'select')
          for in_step, in_index in all_inputs:
              if self.get('flowgraph', flow, in_step, in_index, 'status') == TaskStatus.ERROR:
                  self.logger.error(f'Halting step due to previous error in {in_step}{in_index}')
                  self._haltstep(step, index)
  
              # Skip copying pkg.json files here, since we write the current chip
              # configuration into inputs/{design}.pkg.json earlier in _runstep.
              utils.copytree(f"../../../{in_job}/{in_step}/{in_index}/outputs", 'inputs/', dirs_exist_ok=True,
                  ignore=[f'{design}.pkg.json'], link=True)
  
          ##################
          # 9. Check manifest
          self.set('arg', 'step', step, clobber=True)
          self.set('arg', 'index', index, clobber=True)
  
          if not self.get('option', 'skipcheck'):
              if self.check_manifest():
                  self.logger.error(f"Fatal error in check_manifest()! See previous errors.")
                  self._haltstep(step, index)
  
          ##################
          # 10. Run preprocess step for tool
          if tool not in self.builtin:
              func = self.find_function(tool, "pre_process", 'tools')
              if func:
                  func(self)
                  if self.error:
                      self.logger.error(f"Pre-processing failed for '{tool}'")
                      self._haltstep(step, index)
  
          ##################
          # 11. Set environment variables
  
          # License file configuration.
          for item in self.getkeys('tool', tool, 'licenseserver'):
              license_file = self.get('tool', tool, 'licenseserver', item)
              if license_file:
                  os.environ[item] = ':'.join(license_file)
  
          # Tool-specific environment variables for this task.
          if (step in self.getkeys('tool', tool, 'env')) and \
             (index in self.getkeys('tool', tool, 'env', step)):
              for item in self.getkeys('tool', tool, 'env', step, index):
                  os.environ[item] = self.get('tool', tool, 'env', step, index, item)
  
          ##################
          # 12. Check exe version
  
          vercheck = not self.get('option', 'novercheck')
          veropt = self.get('tool', tool, 'vswitch')
          exe = self._getexe(tool)
          version = None
          toolpath = exe # For record
          if exe is not None:
              exe_path, exe_base = os.path.split(exe)
              if veropt:
                  cmdlist = [exe]
                  cmdlist.extend(veropt)
                  proc = subprocess.run(cmdlist, stdout=PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
                  parse_version = self.find_function(tool, 'parse_version', 'tools')
                  if parse_version is None:
                      self.logger.error(f'{tool} does not implement parse_version.')
                      self._haltstep(step, index)
                  version = parse_version(proc.stdout)
  
                  self.logger.info(f"Tool '{exe_base}' found with version '{version}' in directory '{exe_path}'")
                  if vercheck and not self._check_version(version, tool):
                      self._haltstep(step, index)
              else:
                  self.logger.info(f"Tool '{exe_base}' found in directory '{exe_path}'")
          elif tool not in self.builtin:
              exe_base = self.get('tool', tool, 'exe')
              self.logger.error(f'Executable {exe_base} not found')
              self._haltstep(step, index)
  
          ##################
          # 13. Write manifest (tool interface) (Don't move this!)
          suffix = self.get('tool', tool, 'format')
          if suffix:
              pruneopt = bool(suffix!='tcl')
              self.write_manifest(f"sc_manifest.{suffix}", prune=pruneopt, abspath=True)
  
          ##################
          # 14. Start CPU Timer
          self.logger.debug(f"Starting executable")
          cpu_start = time.time()
  
          ##################
          # 15. Run executable (or copy inputs to outputs for builtin functions)
  
          # TODO: Currently no memory usage tracking in breakpoints, builtins, or unexpected errors.
          max_mem_bytes = 0
  
          if tool in self.builtin:
              utils.copytree(f"inputs", 'outputs', dirs_exist_ok=True, link=True)
          elif not self.get('option', 'skipall'):
              cmdlist = self._makecmd(tool, step, index)
              exe_base = os.path.basename(cmdlist[0])
              cmdstr = ' '.join([exe_base] + cmdlist[1:])
              self.logger.info('Running in %s', workdir)
              self.logger.info('%s', cmdstr)
              timeout = self.get('flowgraph', flow, step, index, 'timeout')
              logfile = step + '.log'
              if sys.platform in ('darwin', 'linux') and step in self.get('option', 'bkpt'):
                  # When we break on a step, the tool often drops into a shell.
                  # However, our usual subprocess scheme seems to break terminal
                  # echo for some tools. On POSIX-compatible systems, we can use
                  # pty to connect the tool to our terminal instead. This code
                  # doesn't handle quiet/timeout logic, since we don't want either
                  # of these features for an interactive session. Logic for
                  # forwarding to file based on
                  # https://docs.python.org/3/library/pty.html#example.
                  logfile = step + '.log'
                  with open(logfile, 'wb') as log_writer:
                      def read(fd):
                          data = os.read(fd, 1024)
                          log_writer.write(data)
                          return data
                      import pty # Note: this import throws exception on Windows
                      retcode = pty.spawn(cmdlist, read)
              else:
                  stdout_file = ''
                  stdout_suffix = self.get('tool', tool, 'stdout', step, index, 'suffix')
                  if self.get('tool', tool, 'stdout', step, index, 'destination') == 'log':
                      stdout_file = step + "." + stdout_suffix
                  elif self.get('tool', tool, 'stdout', step, index, 'destination') == 'output':
                      stdout_file =  os.path.join('outputs', self.get('design')) + "." + stdout_suffix
                  elif self.get('tool', tool, 'stdout', step, index, 'destination') == 'none':
                      stdout_file =  os.devnull
                  else:
                      destination = self.get('tool', tool, 'stdout', step, index, 'destination')
                      self.logger.error(f'stdout/destination has no support for {destination}. Use [log|output|none].')
                      self._haltstep(step, index)
                  stderr_file = ''
                  stderr_suffix = self.get('tool', tool, 'stderr', step, index, 'suffix')
                  if self.get('tool', tool, 'stderr', step, index, 'destination') == 'log':
                      stderr_file = step + "." + stderr_suffix
                  elif self.get('tool', tool, 'stderr', step, index, 'destination') == 'output':
                      stderr_file =  os.path.join('outputs', self.get('design')) + "." + stderr_suffix
                  elif self.get('tool', tool, 'stderr', step, index, 'destination') == 'none':
                      stderr_file =  os.devnull
                  else:
                      destination = self.get('tool', tool, 'stderr', step, index, 'destination')
                      self.logger.error(f'stderr/destination has no support for {destination}. Use [log|output|none].')
                      self._haltstep(step, index)
  
                  with open(stdout_file, 'w') as stdout_writer, open(stdout_file, 'r') as stdout_reader, open(stderr_file, 'w') as stderr_writer, open(stderr_file, 'r') as stderr_reader:
                      # Use separate reader/writer file objects as hack to display
                      # live output in non-blocking way, so we can monitor the
                      # timeout. Based on https://stackoverflow.com/a/18422264.
                      is_stdout_log = self.get('tool', tool, 'stdout', step, index, 'destination') == 'log'
                      is_stderr_log = self.get('tool', tool, 'stderr', step, index, 'destination') == 'log' and stderr_file != stdout_file
                      # if STDOUT and STDERR are to be redirected to the same file,
                      # use a single writer
                      if stderr_file == stdout_file:
                          stderr_writer.close()
                          stderr_reader.close()
                          stderr_writer = subprocess.STDOUT
  
                      cmd_start_time = time.time()
                      proc = subprocess.Popen(cmdlist,
                                              stdout=stdout_writer,
                                              stderr=stderr_writer)
  
                      while proc.poll() is None:
                          # Gather subprocess memory usage.
                          try:
                              pproc = psutil.Process(proc.pid)
                              max_mem_bytes = max(max_mem_bytes, pproc.memory_full_info().uss)
                          except psutil.Error:
                              # Process may have already terminated or been killed.
                              # Retain existing memory usage statistics in this case.
                              pass
  
                          # Loop until process terminates
                          if not quiet:
                              if is_stdout_log:
                                  sys.stdout.write(stdout_reader.read())
                              if is_stderr_log:
                                  sys.stdout.write(stderr_reader.read())
                          if timeout is not None and time.time() - cmd_start_time > timeout:
                              self.logger.error(f'Step timed out after {timeout} seconds')
                              proc.terminate()
                              self._haltstep(step, index)
                          time.sleep(0.1)
  
                      # Read the remaining
                      if not quiet:
                          if is_stdout_log:
                              sys.stdout.write(stdout_reader.read())
                          if is_stderr_log:
                              sys.stdout.write(stderr_reader.read())
                      retcode = proc.returncode
  
              if retcode != 0:
                  self.logger.warning('Command failed with code %d. See log file %s', retcode, os.path.abspath(logfile))
                  self._haltstep(step, index)
  
          ##################
          # 16. Capture cpu runtime and memory footprint.
          cpu_end = time.time()
          cputime = round((cpu_end - cpu_start),2)
          self.set('metric', step, index, 'exetime', cputime)
          self.set('metric', step, index, 'memory', max_mem_bytes)
  
          ##################
          # 17. Post process
          if (tool not in self.builtin) and (not self.get('option', 'skipall')) :
              func = self.find_function(tool, 'post_process', 'tools')
              if func:
                  func(self)
  
          ##################
          # 18. Check log file (must be after post-process)
          if (tool not in self.builtin) and (not self.get('option', 'skipall')) :
              matches = self.check_logfile(step=step, index=index, display=not quiet)
              if 'errors' in matches:
                  self.set('metric', step, index, 'errors', matches['errors'])
              if 'warnings' in matches:
                  self.set('metric', step, index, 'warnings', matches['warnings'])
  
          ##################
          # 19. Hash files
          if self.get('option', 'hash') and (tool not in self.builtin):
              # hash all outputs
              self.hash_files('tool', tool, 'output', step, index)
              # hash all requirements
              if self.valid('tool', tool, 'require', step, index, quiet=True):
                  for item in self.get('tool', tool, 'require', step, index):
                      args = item.split(',')
                      if 'file' in self.get(*args, field='type'):
                          self.hash_files(*args)
  
          ##################
          # 20. Capture wall runtime and cpu cores
          wall_end = time.time()
          walltime = round((wall_end - wall_start),2)
          self.set('metric',step, index, 'tasktime', walltime)
          self.logger.info(f"Finished task in {walltime}s")
  
          ##################
          # 21. Make a record if tracking is enabled
          if self.get('option', 'track'):
              self._make_record(step, index, wall_start, wall_end, version, toolpath, cmdlist[1:])
  
          ##################
          # 22. Save a successful manifest
          self.set('flowgraph', flow, step, index, 'status', TaskStatus.SUCCESS)
          self.set('arg', 'step', None, clobber=True)
          self.set('arg', 'index', None, clobber=True)
  
          self.write_manifest(os.path.join("outputs", f"{design}.pkg.json"))
  
          ##################
          # 23. Stop if there are errors
          errors = self.get('metric', step, index, 'errors')
          if errors > 0 and not self.get('option', 'flowcontinue'):
              self.logger.error(f'{tool} reported {errors} errors during {step}{index}')
              self._haltstep(step, index)
  
          ##################
          # 24. Clean up non-essential files
          if self.get('option', 'clean'):
              self._eda_clean(tool, step, index)
  
          ##################
          # 25. return to original directory
          os.chdir(cwd)
  
      ###########################################################################
      def _haltstep(self, step, index, log=True):
          if log:
              self.logger.error(f"Halting step '{step}' index '{index}' due to errors.")
          sys.exit(1)
  
      ###########################################################################
      def _eda_clean(self, tool, step, index):
          '''Cleans up work directory of unecessary files.
  
          Assumes our cwd is the workdir for step and index.
          '''
  
          keep = ['inputs', 'outputs', 'reports', f'{step}.log', 'replay.sh']
  
          manifest_format = self.get('tool', tool, 'format')
          if manifest_format:
              keep.append(f'sc_manifest.{manifest_format}')
  
          for suffix in self.getkeys('tool', tool, 'regex', step, index):
              keep.append(f'{step}.{suffix}')
  
          # Tool-specific keep files
          if self.valid('tool', tool, 'keep', step, index):
              keep.extend(self.get('tool', tool, 'keep', step, index))
  
          for path in os.listdir():
              if path in keep:
                  continue
              if os.path.isdir(path):
                  shutil.rmtree(path)
              else:
                  os.remove(path)
  
      ###########################################################################
      def run(self):
          '''
          Executes tasks in a flowgraph.
  
          The run function sets up tools and launches runs for every index
          in a step defined by a steplist. The steplist is taken from the schema
          steplist parameter if defined, otherwise the steplist is defined
          as the list of steps within the schema flowgraph dictionary. Before
          starting  the process, tool modules are loaded and setup up for each
          step and index based on on the schema eda dictionary settings.
          Once the tools have been set up, the manifest is checked using the
          check_manifest() function and files in the manifest are hashed based
          on the 'hashmode' schema setting.
  
          Once launched, each process waits for preceding steps to complete,
          as defined by the flowgraph 'inputs' parameter. Once a all inputs
          are ready, previous steps are checked for errors before the
          process entered a local working directory and starts to run
          a tool or to execute a built in Chip function.
  
          Fatal errors within a step/index process cause all subsequent
          processes to exit before start, returning control to the the main
          program which can then exit.
  
          Examples:
              >>> run()
              Runs the execution flow defined by the flowgraph dictionary.
          '''
  
          flow = self.get('option', 'flow')
  
          # Re-init logger to include run info after setting up flowgraph.
          self._init_logger(in_run=True)
  
          # Run steps if set, otherwise run whole graph
          if self.get('arg', 'step'):
              steplist = [self.get('arg', 'step')]
          elif self.get('option', 'steplist'):
              steplist = self.get('option', 'steplist')
          else:
              steplist = self.list_steps()
  
              if not self.get('option', 'resume'):
                  # If no step(list) was specified, the whole flow is being run
                  # start-to-finish. Delete the build dir to clear stale results.
                  cur_job_dir = self._getworkdir()
                  if os.path.isdir(cur_job_dir):
                      shutil.rmtree(cur_job_dir)
  
          # List of indices to run per step. Precomputing this ensures we won't
          # have any problems if [arg, index] gets clobbered, and reduces logic
          # repetition.
          indexlist = {}
          for step in steplist:
              if self.get('arg', 'index'):
                  indexlist[step] = [self.get('arg', 'index')]
              elif self.get('option', 'indexlist'):
                  indexlist[step] = self.get("option", 'indexlist')
              else:
                  indexlist[step] = self.getkeys('flowgraph', flow, step)
  
          # Reset flowgraph/records/metrics by probing build directory. We need
          # to set values to None for steps we may re-run so that merging
          # manifests from _runtask() actually updates values.
          should_resume = self.get("option", 'resume')
          for step in self.getkeys('flowgraph', flow):
              all_indices_failed = True
              for index in self.getkeys('flowgraph', flow, step):
                  stepdir = self._getworkdir(step=step, index=index)
                  cfg = f"{stepdir}/outputs/{self.get('design')}.pkg.json"
  
                  in_steplist = step in steplist and index in indexlist[step]
                  if not os.path.isdir(stepdir) or (in_steplist and not should_resume):
                      # If stepdir doesn't exist, we need to re-run this task. If
                      # we're not running with -resume, we also re-run anything
                      # in the steplist.
                      self.set('flowgraph', flow, step, index, 'status', None)
                      for metric in self.getkeys('metric', 'default', 'default'):
                          self.set('metric', step, index, metric, None)
                      for record in self.getkeys('record', 'default', 'default'):
                          self.set('record', step, index, record, None)
                  elif os.path.isfile(cfg):
                      self.set('flowgraph', flow, step, index, 'status', TaskStatus.SUCCESS)
                      all_indices_failed = False
                  else:
                      self.set('flowgraph', flow, step, index, 'status', TaskStatus.ERROR)
  
              if should_resume and all_indices_failed and step in steplist:
                  # When running with -resume, we re-run any step in steplist that
                  # had all indices fail.
                  for index in self.getkeys('flowgraph', flow, step):
                      if index in indexlist[step]:
                          self.set('flowgraph', flow, step, index, 'status', None)
                          for metric in self.getkeys('metric', 'default', 'default'):
                              self.set('metric', step, index, metric,  None)
                          for record in self.getkeys('record', 'default', 'default'):
                              self.set('record', step, index, record, None)
  
          # Set env variables
          for envvar in self.getkeys('option', 'env'):
              val = self.get('option', 'env', envvar)
              os.environ[envvar] = val
  
          # Remote workflow: Dispatch the Chip to a remote server for processing.
          if self.get('option','remote'):
              # Load the remote storage config into the status dictionary.
              if self.get('option','credentials'):
                  # Use the provided remote credentials file.
                  cfg_file = self.get('option','credentials')[-1]
                  cfg_dir = os.path.dirname(cfg_file)
              else:
                  # Use the default config file path.
                  cfg_dir = os.path.join(Path.home(), '.sc')
                  cfg_file = os.path.join(cfg_dir, 'credentials')
              if (not os.path.isdir(cfg_dir)) or (not os.path.isfile(cfg_file)):
                  self.logger.error('Could not find remote server configuration - please run "sc-configure" and enter your server address and credentials.')
                  raise SiliconCompilerError('Valid remote credentials could not be found.')
              with open(cfg_file, 'r') as cfgf:
                  self.status['remote_cfg'] = json.loads(cfgf.read())
              if (not 'address' in self.status['remote_cfg']):
                  self.logger.error('Improperly formatted remote server configuration - please run "sc-configure" and enter your server address and credentials.')
                  raise SiliconCompilerError('Valid remote credentials could not be found.')
  
              # Pre-process: Run an 'import' stage locally, and upload the
              # in-progress build directory to the remote server.
              # Data is encrypted if user / key were specified.
              # run remote process
              remote_preprocess(self)
  
              # Run the job on the remote server, and wait for it to finish.
              remote_run(self)
  
              # Fetch results (and delete the job's data from the server).
              fetch_results(self)
  
              # Read back configuration from final manifest.
              cfg = os.path.join(self._getworkdir(),f"{self.get('design')}.pkg.json")
              if os.path.isfile(cfg):
                  local_dir = self.get('option','builddir')
                  self.read_manifest(cfg, clobber=True, clear=True)
                  self.set('option', 'builddir', local_dir)
              else:
                  # Hack to find first failed step by checking for presence of
                  # output manifests.
                  # TODO: fetch_results() should return info about step failures.
                  failed_step = steplist[-1]
                  for step in steplist[:-1]:
                      step_has_cfg = False
                      for index in indexlist[step]:
                          stepdir = self._getworkdir(step=step, index=index)
                          cfg = f"{stepdir}/outputs/{self.get('design')}.pkg.json"
                          if os.path.isfile(cfg):
                              step_has_cfg = True
                              break
  
                      if not step_has_cfg:
                          failed_step = step
                          break
  
                  stepdir = self._getworkdir(step=failed_step)[:-1]
                  raise SiliconCompilerError(f'Run() failed on step {failed_step}! '
                      f'See logs in {stepdir} for error details.')
          else:
              status = {}
  
              # Populate status dict with any flowgraph status values that have already
              # been set.
              for step in self.getkeys('flowgraph', flow):
                  for index in self.getkeys('flowgraph', flow, step):
                      stepstr = step + index
                      task_status = self.get('flowgraph', flow, step, index, 'status')
                      if task_status is not None:
                          status[step + index] = task_status
                      else:
                          status[step + index] = TaskStatus.PENDING
  
              # Setup tools for all tasks to run.
              for step in steplist:
                  for index in indexlist[step]:
                      # Setting up tool is optional
                      tool = self.get('flowgraph', flow, step, index, 'tool')
                      if tool not in self.builtin:
                          self.set('arg','step', step)
                          self.set('arg','index', index)
                          func = self.find_function(tool, 'setup', 'tools')
                          if func is None:
                              self.logger.error(f'setup() not found for tool {tool}')
                              sys.exit(1)
                          func(self)
                          # Need to clear index, otherwise we will skip
                          # setting up other indices. Clear step for good
                          # measure.
                          self.set('arg','step', None)
                          self.set('arg','index', None)
  
              # Implement auto-update of jobincrement
              try:
                  alljobs = os.listdir(self.get('option','builddir') + "/" + self.get('design'))
                  if self.get('option','jobincr'):
                      jobid = 0
                      for item in alljobs:
                          m = re.match(self.get('option','jobname')+r'(\d+)', item)
                          if m:
                              jobid = max(jobid, int(m.group(1)))
                      self.set('option', 'jobid', str(jobid + 1))
              except:
                  pass
  
              # Check validity of setup
              self.logger.info("Checking manifest before running.")
              if not self.get('option','skipcheck'):
                  self.check_manifest()
  
              # Check if there were errors before proceeding with run
              if self.error:
                  self.logger.error(f"Check failed. See previous errors.")
                  raise SiliconCompilerError(f"Manifest checks failed.")
  
              # For each task to run, prepare a process and store its dependencies
              jobname = self.get('option','jobname')
              tasks_to_run = {}
              processes = {}
              for step in steplist:
                  for index in indexlist[step]:
                      if status[step+index] != TaskStatus.PENDING:
                          continue
  
                      inputs = [step+index for step, index in self.get('flowgraph', flow, step, index, 'input')]
  
                      if (step in self.getkeys('option','jobinput') and
                          index in self.getkeys('option','jobinput', step) and
                          self.get('option','jobinput', step, index) != jobname):
                          # If we specify a different job as input to this task,
                          # we assume we are good to run it.
                          tasks_to_run[step+index] = []
                      else:
                          tasks_to_run[step+index] = inputs
  
                      processes[step+index] = multiprocessing.Process(target=self._runtask,
                                                                      args=(step, index, status))
  
  
              # We have to deinit the chip's logger before spawning the processes
              # since the logger object is not serializable. _runtask_safe will
              # reinitialize the logger in each new process, and we reinitialize
              # the primary chip's logger after the processes complete.
              self._deinit_logger()
  
              running_tasks = []
              while len(tasks_to_run) > 0 or len(running_tasks) > 0:
                  # Check for new tasks that can be launched.
                  for task, deps in list(tasks_to_run.items()):
                      # TODO: breakpoint logic:
                      # if task is bkpt, then don't launch while len(running_tasks) > 0
  
                      # Clear any tasks that have finished from dependency list.
                      for in_task in deps.copy():
                          if status[in_task] != TaskStatus.PENDING:
                              deps.remove(in_task)
  
                      # If there are no dependencies left, launch this task and
                      # remove from tasks_to_run.
                      if len(deps) == 0:
                          processes[task].start()
                          running_tasks.append(task)
                          del tasks_to_run[task]
  
                  # Check for situation where we have stuff left to run but don't
                  # have any tasks running. This shouldn't happen, but we will get
                  # stuck in an infinite loop if it does, so we want to break out
                  # with an explicit error.
                  if len(tasks_to_run) > 0 and len(running_tasks) == 0:
                      raise SiliconCompilerError('Tasks left to run, but no '
                          'running tasks. Steplist may be invalid.')
  
                  # Check for completed tasks.
                  # TODO: consider staying in this section of loop until a task
                  # actually completes.
                  for task in running_tasks.copy():
                      if not processes[task].is_alive():
                          running_tasks.remove(task)
                          if processes[task].exitcode > 0:
                              status[task] = TaskStatus.ERROR
                          else:
                              status[task] = TaskStatus.SUCCESS
  
                  # TODO: exponential back-off with max?
                  time.sleep(0.1)
  
              self._init_logger()
  
              # Make a clean exit if one of the steps failed
              for step in steplist:
                  index_succeeded = False
                  for index in indexlist[step]:
                      stepstr = step + index
                      if status[stepstr] != TaskStatus.ERROR:
                          index_succeeded = True
                          break
  
                  if not index_succeeded:
                      raise SiliconCompilerError('Run() failed, see previous errors.')
  
              # On success, write out status dict to flowgraph status'. We do this
              # since certain scenarios won't be caught by reading in manifests (a
              # failing step doesn't dump a manifest). For example, if the
              # steplist's final step has two indices and one fails.
              for step in steplist:
                  for index in indexlist[step]:
                      stepstr = step + index
                      if status[stepstr] != TaskStatus.PENDING:
                          self.set('flowgraph', flow, step, index, 'status', status[stepstr])
  
  
              # Merge cfg back from last executed runsteps.
              # Note: any information generated in steps that do not merge into the
              # last step will not be picked up in this chip object.
              # TODO: we might as well fix this? We can add a helper function to
              # find all steps in the steplist that don't lead to others.
  
              laststep = steplist[-1]
              for index in indexlist[laststep]:
                  lastdir = self._getworkdir(step=laststep, index=index)
  
                  # This no-op listdir operation is important for ensuring we have
                  # a consistent view of the filesystem when dealing with NFS.
                  # Without this, this thread is often unable to find the final
                  # manifest of runs performed on job schedulers, even if they
                  # completed successfully. Inspired by:
                  # https://stackoverflow.com/a/70029046.
  
                  os.listdir(os.path.dirname(lastdir))
  
                  lastcfg = f"{lastdir}/outputs/{self.get('design')}.pkg.json"
                  if status[laststep+index] == TaskStatus.SUCCESS:
                      self._read_manifest(lastcfg, clobber=False, partial=True)
                  else:
                      self.set('flowgraph', flow, laststep, index, 'status', TaskStatus.ERROR)
  
          # Clear scratchpad args since these are checked on run() entry
          self.set('arg', 'step', None, clobber=True)
          self.set('arg', 'index', None, clobber=True)
  
          # Store run in history
          self.record_history()
  
          # Storing manifest in job root directory
          filepath =  os.path.join(self._getworkdir(),f"{self.get('design')}.pkg.json")
          self.write_manifest(filepath)
  
      ##########################################################################
      def record_history(self):
          '''
          Copies all non-empty parameters from current job into the history
          dictionary.
          '''
  
          # initialize new dict
          jobname = self.get('option','jobname')
          self.cfg['history'][jobname] = {}
  
          # copy in all empty values of scope job
          allkeys = self.getkeys()
          for key in allkeys:
              # ignore history in case of cumulative history
              if key[0] != 'history':
                  scope = self.get(*key, field='scope')
                  if not self._keypath_empty(key) and (scope == 'job'):
                      self._copyparam(self.cfg,
                                      self.cfg['history'][jobname],
                                      key)
  
      ###########################################################################
      def _copyparam(self, cfgsrc, cfgdst, keypath):
          '''
          Copies a parameter into the manifest history dictionary.
          '''
  
          # 1. decend keypath, pop each key as its used
          # 2. create key if missing in destination dict
          # 3. populate leaf cell when keypath empty
          if keypath:
              key = keypath[0]
              keypath.pop(0)
              if key not in cfgdst.keys():
                  cfgdst[key] = {}
              self._copyparam(cfgsrc[key], cfgdst[key], keypath)
          else:
              for key in cfgsrc.keys():
                  if key not in ('example', 'switch', 'help'):
                      cfgdst[key] = copy.deepcopy(cfgsrc[key])
  
  
      ###########################################################################
      def show(self, filename=None, extra_options=None):
          '''
          Opens a graphical viewer for the filename provided.
  
          The show function opens the filename specified using a viewer tool
          selected based on the file suffix and the 'showtool' schema setup.
          The 'showtool' parameter binds tools with file suffixes, enabling the
          automated dynamic loading of tool setup functions from
          siliconcompiler.tools.<tool>/<tool>.py. Display settings and
          technology settings for viewing the file are read from the
          in-memory chip object schema settings. All temporary render and
          display files are saved in the <build_dir>/_show directory.
  
          The show() command can also be used to display content from an SC
          schema .json filename provided. In this case, the SC schema is
          converted to html and displayed as a 'dashboard' in the browser.
  
          Filenames with .gz and .zip extensions are automatically unpacked
          before being displayed.
  
          Args:
              filename: Name of file to display
  
          Examples:
              >>> show('build/oh_add/job0/export/0/outputs/oh_add.gds')
              Displays gds file with a viewer assigned by 'showtool'
              >>> show('build/oh_add/job0/export/0/outputs/oh_add.pkg.json')
              Displays manifest in the browser
          '''
  
          if extra_options is None:
              extra_options = []
  
          # Finding last layout if no argument specified
          if filename is None:
              self.logger.info('Searching build directory for layout to show.')
              design = self.get('design')
              # TODO: consider a more flexible approach here. I tried doing a
              # reverse search through all steps, but when verification steps are
              # enabled this finds a DEF passed into LVS rather than the GDS
              # Perhaps we could have a way for flows to register their "final"
              # output.
              laststep = 'export'
              lastindex = '0'
              lastdir = self._getworkdir(step=laststep, index=lastindex)
              gds_file= f"{lastdir}/outputs/{design}.gds"
              def_file = f"{lastdir}/outputs/{design}.def"
              if os.path.isfile(gds_file):
                  filename = gds_file
              elif os.path.isfile(def_file):
                  filename = def_file
  
          if filename is None:
              self.logger.error('Unable to automatically find layout in build directory.')
              self.logger.error('Try passing in a full path to show() instead.')
              return 1
  
          self.logger.info('Showing file %s', filename)
  
          # Parsing filepath
          filepath = os.path.abspath(filename)
          basename = os.path.basename(filepath)
          localfile = basename.replace(".gz","")
          filetype = os.path.splitext(localfile)[1].lower().replace(".","")
  
          #Check that file exists
          if not os.path.isfile(filepath):
              self.logger.error(f"Invalid filepath {filepath}.")
              return 1
  
          # Opening file from temp directory
          cwd = os.getcwd()
          showdir = self.get('option','builddir') + "/_show"
          os.makedirs(showdir, exist_ok=True)
          os.chdir(showdir)
  
          # Uncompress file if necessary
          if os.path.splitext(filepath)[1].lower() == ".gz":
              with gzip.open(filepath, 'rb') as f_in:
                  with open(localfile, 'wb') as f_out:
                      shutil.copyfileobj(f_in, f_out)
          else:
              shutil.copy(filepath, localfile)
  
          #Figure out which tool to use for opening data
          if filetype in self.getkeys('option','showtool'):
              # Using env variable and manifest to pass arguments
              os.environ['SC_FILENAME'] = localfile
              # Setting up tool
              tool = self.get('option','showtool', filetype)
              step = 'show'+filetype
              index = "0"
              self.set('arg', 'step', step)
              self.set('arg', 'index', index)
              setup_tool = self.find_function(tool, 'setup', 'tools')
              setup_tool(self, mode='show')
              self.write_manifest("sc_manifest.tcl", abspath=True)
              self.write_manifest("sc_manifest.json", abspath=True)
              self.set('arg', 'step', None)
              self.set('arg', 'index', None)
  
              exe = self._getexe(tool)
              if shutil.which(exe) is None:
                  self.logger.error(f'Executable {exe} not found.')
                  success = False
              else:
                  # Running command
                  cmdlist = self._makecmd(tool, step, index, extra_options=extra_options)
                  proc = subprocess.run(cmdlist)
                  success = proc.returncode == 0
          else:
              self.logger.error(f"Filetype '{filetype}' not set up in 'showtool' parameter.")
              success = False
  
          # Returning to original directory
          os.chdir(cwd)
          return success
  
      def read_lef(self, path, pdkname, stackup):
          '''Reads tech LEF and imports data into schema.
  
          This function reads layer information from a provided tech LEF and uses
          it to fill out the 'pdk', <pdkname>, 'grid' keypaths of the current chip
          object.
  
          Args:
              path (str): Path to LEF file.
              pdkname (str): Name of PDK associated with LEF file.
              stackup (str): Stackup associated with LEF file.
          '''
          data = leflib.parse(path)
          layer_index = 1
          for name, layer in data['layers'].items():
              if layer['type'] != 'ROUTING':
                  # Skip non-routing layers
                  continue
  
              sc_name = f'm{layer_index}'
              layer_index += 1
              self.set('pdk', pdkname, 'grid', stackup, name, 'name', sc_name)
  
              direction = None
              if 'direction' in layer:
                  direction = layer['direction'].lower()
                  self.set('pdk', pdkname, 'grid', stackup, name, 'dir', direction)
  
              if 'offset' in layer:
                  offset = layer['offset']
                  if isinstance(offset, float):
                      # Per LEF spec, a single offset value applies to the
                      # preferred routing direction. If one doesn't exist, we'll
                      # just ignore.
                      if direction == 'vertical':
                          self.set('pdk', pdkname, 'grid', stackup, name, 'xoffset', offset)
                      elif direction == 'horizontal':
                          self.set('pdk', pdkname, 'grid', stackup, name, 'yoffset', offset)
                  else:
                      xoffset, yoffset = offset
                      self.set('pdk', pdkname, 'grid', stackup, name, 'xoffset', xoffset)
                      self.set('pdk', pdkname, 'grid', stackup, name, 'yoffset', yoffset)
  
              if 'pitch' in layer:
                  pitch = layer['pitch']
                  if isinstance(pitch, float):
                      # Per LEF spec, a single pitch value applies to both
                      # directions.
                      self.set('pdk', pdkname, 'grid', stackup, name, 'xpitch', pitch)
                      self.set('pdk', pdkname, 'grid', stackup, name, 'ypitch', pitch)
                  else:
                      xpitch, ypitch = pitch
                      self.set('pdk', pdkname, 'grid', stackup, name, 'xpitch', xpitch)
                      self.set('pdk', pdkname, 'grid', stackup, name, 'ypitch', ypitch)
  
      ############################################################################
      # Chip helper Functions
      ############################################################################
      def _typecheck(self, cfg, leafkey, value):
          ''' Schema type checking
          '''
          ok = True
          valuetype = type(value)
          errormsg = ""
          if (not re.match(r'\[',cfg['type'])) & (valuetype==list):
              errormsg = "Value must be scalar."
              ok = False
              # Iterate over list
          else:
              # Create list for iteration
              if valuetype == list:
                  valuelist = value
              else:
                  valuelist = [value]
                  # Make type python compatible
              cfgtype = re.sub(r'[\[\]]', '', cfg['type'])
              for item in valuelist:
                  valuetype =  type(item)
                  if ((cfgtype != valuetype.__name__) and (item is not None)):
                      tupletype = re.match(r'\([\w\,]+\)',cfgtype)
                      #TODO: check tuples!
                      if tupletype:
                          pass
                      elif cfgtype == 'bool':
                          if not item in ['true', 'false']:
                              errormsg = "Valid boolean values are True/False/'true'/'false'"
                              ok = False
                      elif cfgtype == 'file':
                          pass
                      elif cfgtype == 'dir':
                          pass
                      elif (cfgtype == 'float'):
                          try:
                              float(item)
                          except:
                              errormsg = "Type mismatch. Cannot cast item to float."
                              ok = False
                      elif (cfgtype == 'int'):
                          try:
                              int(item)
                          except:
                              errormsg = "Type mismatch. Cannot cast item to int."
                              ok = False
                      elif item is not None:
                          errormsg = "Type mismach."
                          ok = False
  
          # Logger message
          if type(value) == list:
              printvalue = ','.join(map(str, value))
          else:
              printvalue = str(value)
          errormsg = (errormsg +
                      " Key=" + str(leafkey) +
                      ", Expected Type=" + cfg['type'] +
                      ", Entered Type=" + valuetype.__name__ +
                      ", Value=" + printvalue)
  
  
          return (ok, errormsg)
  
      #######################################
      def _getexe(self, tool):
          path = self.get('tool', tool, 'path')
          exe = self.get('tool', tool, 'exe')
          if exe is None:
              return None
  
          syspath = os.getenv('PATH', os.defpath)
          if path:
              # Prepend 'path' schema var to system path
              syspath = self._resolve_env_vars(path) + os.pathsep + syspath
  
          fullexe = shutil.which(exe, path=syspath)
  
          return fullexe
  
      #######################################
      def _makecmd(self, tool, step, index, extra_options=None):
          '''
          Constructs a subprocess run command based on eda tool setup.
          Creates a replay script in current directory.
          '''
  
          fullexe = self._getexe(tool)
  
          options = []
          is_posix = (sys.platform != 'win32')
  
          for option in self.get('tool', tool, 'option', step, index):
              options.extend(shlex.split(option, posix=is_posix))
  
          # Add scripts files
          if self.valid('tool', tool, 'script', step, index):
              scripts = self.find_files('tool', tool, 'script', step, index)
          else:
              scripts = []
  
          cmdlist = [fullexe]
          if extra_options:
              cmdlist.extend(extra_options)
          cmdlist.extend(options)
          cmdlist.extend(scripts)
          runtime_options = self.find_function(tool, 'runtime_options', 'tools')
          if runtime_options:
              for option in runtime_options(self):
                  cmdlist.extend(shlex.split(option, posix=is_posix))
  
          envvars = {}
          for key in self.getkeys('option','env'):
              envvars[key] = self.get('option','env', key)
          for item in self.getkeys('tool', tool, 'licenseserver'):
              license_file = self.get('tool', tool, 'licenseserver', item)
              if license_file:
                  envvars[item] = ':'.join(license_file)
          if self.get('tool', tool, 'path'):
              envvars['PATH'] = self.get('tool', tool, 'path') + os.pathsep + '$PATH'
          if (step in self.getkeys('tool', tool, 'env') and
              index in self.getkeys('tool', tool, 'env', step)):
              for key in self.getkeys('tool', tool, 'env', step, index):
                  envvars[key] = self.get('tool', tool, 'env', step, index, key)
  
          #create replay file
          script_name = 'replay.sh'
          with open(script_name, 'w') as f:
              print('#!/bin/bash', file=f)
  
              envvar_cmd = 'export'
              for key, val in envvars.items():
                  print(f'{envvar_cmd} {key}={val}', file=f)
  
              replay_cmdlist = [os.path.basename(cmdlist[0])] + cmdlist[1:]
              print(' '.join(f'"{arg}"' if ' ' in arg else arg for arg in replay_cmdlist), file=f)
          os.chmod(script_name, 0o755)
  
          return cmdlist
  
      #######################################
      def _get_cloud_region(self):
          # TODO: add logic to figure out if we're running on a remote cluster and
          # extract the region in a provider-specific way.
          return 'local'
  
      #######################################
      def _make_record(self, step, index, start, end, toolversion, toolpath, cli_args):
          '''
          Records provenance details for a runstep.
          '''
          self.set('record', step, index, 'scversion', _metadata.version)
  
          start_date = datetime.datetime.fromtimestamp(start).strftime('%Y-%m-%d %H:%M:%S')
          end_date = datetime.datetime.fromtimestamp(end).strftime('%Y-%m-%d %H:%M:%S')
  
          userid = getpass.getuser()
          self.set('record', step, index, 'userid', userid)
  
          if toolversion:
              self.set('record', step, index, 'toolversion', toolversion)
  
          self.set('record', step, index, 'starttime', start_date)
          self.set('record', step, index, 'endtime', end_date)
  
          machine = platform.node()
          self.set('record', step, index, 'machine', machine)
  
          self.set('record', step, index, 'region', self._get_cloud_region())
  
          try:
              gateways = netifaces.gateways()
              ipaddr, interface = gateways['default'][netifaces.AF_INET]
              macaddr = netifaces.ifaddresses(interface)[netifaces.AF_LINK][0]['addr']
              self.set('record', step, index, 'ipaddr', ipaddr)
              self.set('record', step, index, 'macaddr', macaddr)
          except KeyError:
              self.logger.warning('Could not find default network interface info')
  
          system = platform.system()
          if system == 'Darwin':
              lower_sys_name = 'macos'
          else:
              lower_sys_name = system.lower()
          self.set('record', step, index, 'platform', lower_sys_name)
  
          if system == 'Linux':
              distro_name = distro.id()
              self.set('record', step, index, 'distro', distro_name)
  
          if system == 'Darwin':
              osversion, _, _ = platform.mac_ver()
          elif system == 'Linux':
              osversion = distro.version()
          else:
              osversion = platform.release()
          self.set('record', step, index, 'osversion', osversion)
  
          if system == 'Linux':
              kernelversion = platform.release()
          elif system == 'Windows':
              kernelversion = platform.version()
          elif system == 'Darwin':
              kernelversion = platform.release()
          else:
              kernelversion = None
          if kernelversion:
              self.set('record', step, index, 'kernelversion', kernelversion)
  
          arch = platform.machine()
          self.set('record', step, index, 'arch', arch)
  
          self.set('record', step, index, 'toolpath', toolpath)
  
          toolargs = ' '.join(f'"{arg}"' if ' ' in arg else arg for arg in cli_args)
          self.set('record', step, index, 'toolargs', toolargs)
  
      #######################################
      def _safecompare(self, value, op, goal):
          # supported relational oprations
          # >, >=, <=, <. ==, !=
          if op == ">":
              return(bool(value>goal))
          elif op == ">=":
              return(bool(value>=goal))
          elif op == "<":
              return(bool(value<goal))
          elif op == "<=":
              return(bool(value<=goal))
          elif op == "==":
              return(bool(value==goal))
          elif op == "!=":
              return(bool(value!=goal))
          else:
              self.error = 1
              self.logger.error(f"Illegal comparison operation {op}")
  
  
      #######################################
      def _getworkdir(self, jobname=None, step=None, index='0'):
          '''Create a step directory with absolute path
          '''
  
          if jobname is None:
              jobname = self.get('option','jobname')
  
          dirlist =[self.cwd,
                    self.get('option','builddir'),
                    self.get('design'),
                    jobname]
  
          # Return jobdirectory if no step defined
          # Return index 0 by default
          if step is not None:
              dirlist.append(step)
              dirlist.append(index)
  
          return os.path.join(*dirlist)
  
      #######################################
      def _resolve_env_vars(self, filepath):
          resolved_path = os.path.expandvars(filepath)
  
          # variables that don't exist in environment get ignored by `expandvars`,
          # but we can do our own error checking to ensure this doesn't result in
          # silent bugs
          envvars = re.findall(r'\$(\w+)', resolved_path)
          for var in envvars:
              self.logger.warning(f'Variable {var} in {filepath} not defined in environment')
  
          return resolved_path
  
      #######################################
      def _get_imported_filename(self, pathstr):
          ''' Utility to map collected file to an unambigious name based on its path.
  
          The mapping looks like:
          path/to/file.ext => file_<md5('path/to/file.ext')>.ext
          '''
          path = pathlib.Path(pathstr)
          ext = ''.join(path.suffixes)
  
          # strip off all file suffixes to get just the bare name
          while path.suffix:
              path = pathlib.Path(path.stem)
          filename = str(path)
  
          pathhash = hashlib.sha1(pathstr.encode('utf-8')).hexdigest()
  
          return f'{filename}_{pathhash}{ext}'
  
      def _check_version(self, reported_version, tool):
          # Based on regex for deprecated "legacy specifier" from PyPA packaging
          # library. Use this to parse PEP-440ish specifiers with arbitrary
          # versions.
          _regex_str = r"""
              (?P<operator>(==|!=|<=|>=|<|>|~=))
              \s*
              (?P<version>
                  [^,;\s)]* # Since this is a "legacy" specifier, and the version
                            # string can be just about anything, we match everything
                            # except for whitespace, a semi-colon for marker support,
                            # a closing paren since versions can be enclosed in
                            # them, and a comma since it's a version separator.
              )
              """
          _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
  
          normalize_version = self.find_function(tool, 'normalize_version', 'tools')
          # Version is good if it matches any of the specifier sets in this list.
          spec_sets = self.get('tool', tool, 'version')
  
          for spec_set in spec_sets:
              split_specs = [s.strip() for s in spec_set.split(",") if s.strip()]
              specs_list = []
              for spec in split_specs:
                  match = re.match(_regex, spec)
                  if match is None:
                      self.logger.warning(f'Invalid version specifier {spec}. Defaulting to =={spec}.')
                      operator = '=='
                      spec_version = spec
                  else:
                      operator = match.group('operator')
                      spec_version = match.group('version')
                  specs_list.append((operator, spec_version))
  
              if normalize_version is None:
                  normalized_version = reported_version
                  normalized_specs = ','.join([f'{op}{ver}' for op, ver in specs_list])
              else:
                  normalized_version = normalize_version(reported_version)
                  normalized_specs = ','.join([f'{op}{normalize_version(ver)}' for op, ver in specs_list])
  
              try:
                  version = packaging.version.Version(normalized_version)
              except packaging.version.InvalidVersion:
                  self.logger.error(f'Version {reported_version} reported by {tool} does not match standard.')
                  if normalize_version is None:
                      self.logger.error('Tool driver should implement normalize_version().')
                  else:
                      self.logger.error(f'normalize_version() returned invalid version {normalized_version}')
  
                  return False
  
              try:
                  spec_set = packaging.specifiers.SpecifierSet(normalized_specs)
              except packaging.specifiers.InvalidSpecifier:
                  self.logger.error(f'Version specifier set {normalized_specs} does not match standard.')
                  return False
  
              if version in spec_set:
                  return True
  
          allowedstr = '; '.join(spec_sets)
          self.logger.error(f"Version check failed for {tool}. Check installation.")
          self.logger.error(f"Found version {reported_version}, did not satisfy any version specifier set {allowedstr}.")
          return False
  
  
  ###############################################################################
  # Package Customization classes
  ###############################################################################
  
  class YamlIndentDumper(yaml.Dumper):
      def increase_indent(self, flow=False, indentless=False):
          return super(YamlIndentDumper, self).increase_indent(flow, False)
  
  class SiliconCompilerError(Exception):
      ''' Minimal Exception wrapper used to raise sc runtime errors.
      '''
      def __init__(self, message):
          super(Exception, self).__init__(message)
  
files: siliconcompiler/core.py
files: # Copyright 2020 Silicon Compiler Authors. All Rights Reserved.
  
  import argparse
  import base64
  import time
  import datetime
  import multiprocessing
  import tarfile
  import traceback
  import asyncio
  from subprocess import run, PIPE
  import os
  import glob
  import pathlib
  import sys
  import gzip
  import re
  import json
  import logging
  import hashlib
  import shutil
  import copy
  import importlib
  import textwrap
  import math
  import pandas
  import yaml
  import graphviz
  import time
  import uuid
  import shlex
  import platform
  import getpass
  import csv
  import distro
  import netifaces
  import webbrowser
  import packaging.version
  import packaging.specifiers
  from jinja2 import Environment, FileSystemLoader
  from pathlib import Path
  from timeit import default_timer as timer
  from siliconcompiler.client import *
  from siliconcompiler.schema import *
  from siliconcompiler.scheduler import _deferstep
  from siliconcompiler import leflib
  from siliconcompiler import utils
  from siliconcompiler import _metadata
  import psutil
  
  class TaskStatus():
      # Could use Python 'enum' class here, but that doesn't work nicely with
      # schema.
      PENDING = 'pending'
      SUCCESS = 'success'
      ERROR = 'error'
  
  class Chip:
      """Object for configuring and executing hardware design flows.
  
      This is the main object used for configuration, data, and
      execution within the SiliconCompiler platform.
  
      Args:
          design (string): Name of the top level chip design module.
  
      Examples:
          >>> siliconcompiler.Chip(design="top")
          Creates a chip object with name "top".
      """
  
      ###########################################################################
      def __init__(self, design, loglevel=None):
          # version numbers
          self.scversion = _metadata.version
          self.schemaversion = SCHEMA_VERSION
  
          # Local variables
          self.scroot = os.path.dirname(os.path.abspath(__file__))
          self.cwd = os.getcwd()
          self.error = 0
          self.cfg = schema_cfg()
          # The 'status' dictionary can be used to store ephemeral config values.
          # Its contents will not be saved, and can be set by parent scripts
          # such as a web server or supervisor process. Currently supported keys:
          # * 'jobhash': A hash or UUID which can identify jobs in a larger system.
          # * 'remote_cfg': Dictionary containing remote server configurations
          #                 (address, credentials, etc.)
          # * 'slurm_account': User account ID in a connected slurm HPC cluster.
          # * 'slurm_partition': Name of the partition in which a task should run
          #                      on a connected slurm HPC cluster.
          # * 'watchdog': Activity-monitoring semaphore for jobs scheduled on an
          #               HPC cluster; expects a 'threading.Event'-like object.
          # * 'max_fs_bytes': A limit on how much disk space a job is allowed
          #                   to consume in a connected HPC cluster's storage.
          self.status = {}
  
          self.builtin = ['minimum','maximum',
                          'nop', 'mux', 'join', 'verify']
  
          # We set 'design' and 'loglevel' directly in the config dictionary
          # because of a chicken-and-egg problem: self.set() relies on the logger,
          # but the logger relies on these values.
          self.cfg['design']['value'] = design
          if loglevel:
              self.cfg['option']['loglevel']['value'] = loglevel
  
          self._init_logger()
  
          self._loaded_modules = {
              'flows': [],
              'pdks': [],
              'libs': [],
              'checklists': []
          }
  
      ###########################################################################
      @property
      def design(self):
          '''Design name of chip object.
  
          This is an immutable property.'''
          return self.get('design')
  
      ###########################################################################
      def _init_logger(self, step=None, index=None, in_run=False):
  
          self.logger = logging.getLogger(uuid.uuid4().hex)
  
          # Don't propagate log messages to "root" handler (we get duplicate
          # messages without this)
          # TODO: this prevents us from being able to capture logs with pytest:
          # we should revisit it
          self.logger.propagate = False
  
          loglevel = self.get('option', 'loglevel')
  
          if loglevel=='DEBUG':
              prefix = '| %(levelname)-7s | %(funcName)-10s | %(lineno)-4s'
          else:
              prefix = '| %(levelname)-7s'
  
          if in_run:
              flow = self.get('option', 'flow')
  
              # Figure out how wide to make step and index fields
              max_step_len = 2
              max_index_len = 2
              for future_step in self.getkeys('flowgraph', flow):
                  max_step_len = max(len(future_step) + 1, max_step_len)
                  for future_index in self.getkeys('flowgraph', flow, future_step):
                      max_index_len = max(len(future_index) + 1, max_index_len)
  
              jobname = self.get('option', 'jobname')
  
              if step is None:
                  step = '-' * max(max_step_len // 4, 1)
              if index is None:
                  index = '-' * max(max_index_len // 4, 1)
  
              run_info = f'%s  | %-{max_step_len}s | %-{max_index_len}s' % (jobname, step, index)
              logformat = ' | '.join([prefix, run_info, '%(message)s'])
          else:
              logformat = ' | '.join([prefix, '%(message)s'])
  
          handler = logging.StreamHandler()
          formatter = logging.Formatter(logformat)
  
          handler.setFormatter(formatter)
  
          # Clear any existing handlers so we don't end up with duplicate messages
          # if repeat calls to _init_logger are made
          if len(self.logger.handlers) > 0:
              self.logger.handlers.clear()
  
          self.logger.addHandler(handler)
          self.logger.setLevel(loglevel)
  
      ###########################################################################
      def _deinit_logger(self):
          self.logger = None
  
      ###########################################################################
      def _get_switches(self, *keypath):
          '''Helper function for parsing switches and metavars for a keypath.'''
          #Switch field fully describes switch format
          switch = self.get(*keypath, field='switch')
  
          if switch is None:
              switches = []
          elif isinstance(switch, list):
              switches = switch
          else:
              switches = [switch]
          switchstrs = []
  
          # parse out switch from metavar
          # TODO: should we validate that metavar matches for each switch?
          for switch in switches:
              switchmatch = re.match(r'(-[\w_]+)\s+(.*)', switch)
              gccmatch = re.match(r'(-[\w_]+)(.*)', switch)
              plusmatch = re.match(r'(\+[\w_\+]+)(.*)', switch)
  
              if switchmatch:
                  switchstr = switchmatch.group(1)
                  metavar = switchmatch.group(2)
              elif gccmatch:
                  switchstr = gccmatch.group(1)
                  metavar = gccmatch.group(2)
              elif plusmatch:
                  switchstr = plusmatch.group(1)
                  metavar = plusmatch.group(2)
              switchstrs.append(switchstr)
  
          return switchstrs, metavar
  
      ###########################################################################
      def create_cmdline(self, progname, description=None, switchlist=None, input_map=None):
          """Creates an SC command line interface.
  
          Exposes parameters in the SC schema as command line switches,
          simplifying creation of SC apps with a restricted set of schema
          parameters exposed at the command line. The order of command
          line switch settings parsed from the command line is as follows:
  
           1. loglevel
           2. fpga_partname
           3. load_target('target')
           4. read_manifest([cfg])
           5. all other switches
  
          The cmdline interface is implemented using the Python argparse package
          and the following use restrictions apply.
  
          * Help is accessed with the '-h' switch.
          * Arguments that include spaces must be enclosed with double quotes.
          * List parameters are entered individually. (ie. -y libdir1 -y libdir2)
          * For parameters with Boolean types, the switch implies "true".
          * Special characters (such as '-') must be enclosed in double quotes.
          * Compiler compatible switches include: -D, -I, -O{0,1,2,3}
          * Verilog legacy switch formats are supported: +libext+, +incdir+
  
          Args:
              progname (str): Name of program to be executed.
              description (str): Short program description.
              switchlist (list of str): List of SC parameter switches to expose
                  at the command line. By default all SC schema switches are
                  available. Parameter switches should be entered based on the
                  parameter 'switch' field in the schema. For parameters with
                  multiple switches, both will be accepted if any one is included
                  in this list.
              input_map (dict of str): Dictionary mapping file extensions to input
                  filetypes. This is used to automatically assign positional
                  source arguments to ['input', ...] keypaths based on their file
                  extension. If None, the CLI will not accept positional source
                  arguments.
  
          Examples:
              >>> chip.create_cmdline(progname='sc-show',switchlist=['-input','-cfg'])
              Creates a command line interface for 'sc-show' app.
  
              >>> chip.create_cmdline(progname='sc', input_map={'v': 'verilog'})
              All sources ending in .v will be stored in ['input', 'verilog']
          """
  
          # Argparse
          parser = argparse.ArgumentParser(prog=progname,
                                           prefix_chars='-+',
                                           formatter_class=argparse.RawDescriptionHelpFormatter,
                                           description=description)
  
          # Get all keys from global dictionary or override at command line
          allkeys = self.getkeys()
  
          # Iterate over all keys to add parser arguments
          for keypath in allkeys:
              #Fetch fields from leaf cell
              helpstr = self.get(*keypath, field='shorthelp')
              typestr = self.get(*keypath, field='type')
  
              # argparse 'dest' must be a string, so join keypath with commas
              dest = '_'.join(keypath)
  
              switchstrs, metavar = self._get_switches(*keypath)
  
              # Three switch types (bool, list, scalar)
              if not switchlist or any(switch in switchlist for switch in switchstrs):
                  if typestr == 'bool':
                      parser.add_argument(*switchstrs,
                                          nargs='?',
                                          metavar=metavar,
                                          dest=dest,
                                          const='true',
                                          help=helpstr,
                                          default=argparse.SUPPRESS)
                  #list type arguments
                  elif re.match(r'\[', typestr):
                      #all the rest
                      parser.add_argument(*switchstrs,
                                          metavar=metavar,
                                          dest=dest,
                                          action='append',
                                          help=helpstr,
                                          default=argparse.SUPPRESS)
                  else:
                      #all the rest
                      parser.add_argument(*switchstrs,
                                          metavar=metavar,
                                          dest=dest,
                                          help=helpstr,
                                          default=argparse.SUPPRESS)
  
          if input_map is not None:
              parser.add_argument('source',
                                  nargs='*',
                                  help='Input files with filetype inferred by extension')
  
          #Preprocess sys.argv to enable linux commandline switch formats
          #(gcc, verilator, etc)
          scargs = []
  
          # Iterate from index 1, otherwise we end up with script name as a
          # 'source' positional argument
          for item in sys.argv[1:]:
              #Split switches with one character and a number after (O0,O1,O2)
              opt = re.match(r'(\-\w)(\d+)', item)
              #Split assign switches (-DCFG_ASIC=1)
              assign = re.search(r'(\-\w)(\w+\=\w+)', item)
              #Split plusargs (+incdir+/path)
              plusarg = re.search(r'(\+\w+\+)(.*)', item)
              if opt:
                  scargs.append(opt.group(1))
                  scargs.append(opt.group(2))
              elif plusarg:
                  scargs.append(plusarg.group(1))
                  scargs.append(plusarg.group(2))
              elif assign:
                  scargs.append(assign.group(1))
                  scargs.append(assign.group(2))
              else:
                  scargs.append(item)
  
          parser.add_argument('-version', action='version', version=_metadata.version)
  
          #Grab argument from pre-process sysargs
          cmdargs = vars(parser.parse_args(scargs))
  
          # Print banner
          print(_metadata.banner)
          print("Authors:", ", ".join(_metadata.authors))
          print("Version:", _metadata.version, "\n")
          print("-"*80)
  
          os.environ["COLUMNS"] = '80'
  
          # 1. set loglevel if set at command line
          if 'option_loglevel' in cmdargs.keys():
              self.logger.setLevel(cmdargs['option_loglevel'])
  
          # 2. read in target if set
          if 'option_target' in cmdargs.keys():
              if 'arg_pdk' in cmdargs.keys():
                  raise NotImplementedError("NOT IMPLEMENTED: ['arg', 'pdk'] parameter with target")
              if 'arg_flow' in cmdargs.keys():
                  raise NotImplementedError("NOT IMPLEMENTED: ['arg', 'flow'] parameter with target")
              if 'fpga_partname' in cmdargs.keys():
                  self.set('fpga', 'partname', cmdargs['fpga_partname'], clobber=True)
              # running target command
              self.load_target(cmdargs['option_target'])
  
          # 4. read in all cfg files
          if 'option_cfg' in cmdargs.keys():
              for item in cmdargs['option_cfg']:
                  self.read_manifest(item, clobber=True, clear=True)
  
          # Map sources to ['input'] keypath.
          if 'source' in cmdargs:
              for source in cmdargs['source']:
                  _, ext = os.path.splitext(source)
                  ext = ext.lstrip('.')
                  if ext in input_map:
                      filetype = input_map[ext]
                      if self.valid('input', filetype, quiet=True):
                          self.add('input', filetype, source)
                      else:
                          self.set('input', filetype, source)
                      self.logger.info(f'Source {source} inferred as {filetype}')
                  else:
                      self.logger.warning('Unable to infer input type for '
                          f'{source} based on file extension, ignoring. Use the '
                          '-input flag to provide it explicitly.')
              # we don't want to handle this in the next loop
              del cmdargs['source']
  
          # 5. Cycle through all command args and write to manifest
          for dest, vals in cmdargs.items():
              keypath = dest.split('_')
  
              # Turn everything into a list for uniformity
              if not isinstance(vals, list):
                  vals = [vals]
  
              # Cycle through all items
              for item in vals:
                  # Hack to handle the fact that we want optmode stored with an 'O'
                  # prefix.
                  if keypath == ['option', 'optmode']:
                      item = 'O' + item
  
                  num_free_keys = keypath.count('default')
  
                  if len(item.split(' ')) < num_free_keys + 1:
                      # Error out if value provided doesn't have enough words to
                      # fill in 'default' keys.
                      switches, metavar = self._get_switches(*keypath)
                      switchstr = '/'.join(switches)
                      self.logger.error(f'Invalid value {item} for switch {switchstr}. Expected format {metavar}.')
                      raise SiliconCompilerError('Invalid CLI arguments')
  
                  # We replace 'default' in keypath with first N words in provided
                  # value. Remainder is the actual value we want to store in the
                  # parameter.
                  *free_keys, val = item.split(' ', num_free_keys)
                  args = [free_keys.pop(0) if key == 'default' else key for key in keypath]
  
                  # Storing in manifest
                  self.logger.info(f"Command line argument entered: {args} Value: {val}")
                  typestr = self.get(*keypath, field='type')
                  if typestr.startswith('['):
                      if self.valid(*args, quiet=True):
                          self.add(*args, val)
                      else:
                          self.set(*args, val, clobber=True)
                  else:
                      self.set(*args, val, clobber=True)
  
      #########################################################################
      def find_function(self, modulename, funcname, moduletype=None):
          '''
          Returns a function attribute from a module on disk.
  
          Searches the SC root directory and the 'scpath' parameter for the
          modulename provided and imports the module if found. If the funcname
          provided is found in the module, a callable function attribute is
          returned, otherwise None is returned.
  
          The function assumes the following directory structure:
  
          * tools/modulename/modulename.py
          * flows/modulename.py
          * pdks/modulname.py
  
          If the moduletype is None, the module paths are search in the
          order: 'targets'->'flows'->'tools'->'pdks'->'libs'->'checklists'):
  
  
          Supported functions include:
  
          * targets (make_docs, setup)
          * pdks (make_docs, setup)
          * flows (make_docs, setup)
          * tools (make_docs, setup, check_version, runtime_options,
            pre_process, post_process)
          * libs (make_docs, setup)
  
          Args:
              modulename (str): Name of module to import.
              funcname (str): Name of the function to find within the module.
              moduletype (str): Type of module (flows, pdks, libs, checklists, targets).
  
          Examples:
              >>> setup_pdk = chip.find_function('freepdk45', 'setup', 'pdks')
              >>> setup_pdk()
              Imports the freepdk45 module and runs the setup_pdk function
  
          '''
  
          # module search path depends on modtype
          if moduletype is None:
              for item in ('targets', 'flows', 'tools', 'pdks', 'libs', 'checklists'):
                  fullpath = self.find_function(modulename, funcname, module_type=item)
                  if fullpath:
                      break
                  self.logger.error(f"Could not find module {modulename}")
                  self.error = 1
                  return None
          elif moduletype in ('targets','flows', 'pdks', 'libs'):
              fullpath = self._find_sc_file(f"{moduletype}/{modulename}.py", missing_ok=True)
          elif moduletype in ('tools', 'checklists'):
              fullpath = self._find_sc_file(f"{moduletype}/{modulename}/{modulename}.py", missing_ok=True)
          else:
              self.logger.error(f"Illegal module type '{moduletype}'.")
              self.error = 1
              return
  
          # try loading module if found
          if fullpath:
              self.logger.debug(f"Loading function '{funcname}' from module '{modulename}'")
  
              try:
                  spec = importlib.util.spec_from_file_location(modulename, fullpath)
                  imported = importlib.util.module_from_spec(spec)
                  spec.loader.exec_module(imported)
  
                  if hasattr(imported, funcname):
                      function = getattr(imported, funcname)
                  else:
                      function = None
                  return function
              except:
                  traceback.print_exc()
                  self.logger.error(f"Module setup failed for '{modulename}'")
                  self.error = 1
  
      ##########################################################################
      def load_target(self, name):
          """
          Loads a target module and runs the setup() function.
  
          The function searches the $SCPATH for targets/<name>.py and runs
          the setup function in that module if found.
  
          Args:
              name (str): Module name
              flow (str): Target flow to
  
          Examples:
              >>> chip.load_target('freepdk45_demo')
              Loads the 'freepdk45_demo' target
  
          """
  
          self.set('option', 'target', name)
  
          func = self.find_function(name, 'setup', 'targets')
          if func is not None:
              func(self)
          else:
              self.logger.error(f'Target module {name} not found in $SCPATH or siliconcompiler/targets/.')
              raise SiliconCompilerError(f'Target module {name} not found $SCPATH or siliconcompiler/targets/.')
  
      ##########################################################################
      def load_pdk(self, name):
          """
          Loads a PDK module and runs the setup() function.
  
          The function searches the $SCPATH for pdks/<name>.py and runs
          the setup function in that module if found.
  
          Args:
              name (str): Module name
  
          Examples:
              >>> chip.load_pdk('freepdk45_pdk')
              Loads the 'freepdk45' pdk
  
          """
  
          func = self.find_function(name, 'setup', 'pdks')
          if func is not None:
              self.logger.info(f"Loading PDK '{name}'")
              self._loaded_modules['pdks'].append(name)
              func(self)
          else:
              self.logger.error(f'PDK module {name} not found in $SCPATH or siliconcompiler/pdks/.')
              raise SiliconCompilerError(f'PDK module {name} not found in $SCPATH or siliconcompiler/pdks/.')
  
      ##########################################################################
      def load_flow(self, name):
          """
          Loads a flow  module and runs the setup() function.
  
          The function searches the $SCPATH for flows/<name>.py and runs
          the setup function in that module if found.
  
          Args:
              name (str): Module name
  
          Examples:
              >>> chip.load_flow('asicflow')
              Loads the 'asicflow' flow
  
          """
  
          func = self.find_function(name, 'setup', 'flows')
          if func is not None:
              self.logger.info(f"Loading flow '{name}'")
              self._loaded_modules['flows'].append(name)
              func(self)
          else:
              self.logger.error(f'Flow module {name} not found in $SCPATH or siliconcompiler/flows/.')
              raise SiliconCompilerError(f'Flow module {name} not found in $SCPATH or siliconcompiler/flows/.')
  
      ##########################################################################
      def load_lib(self, name):
          """
          Loads a library module and runs the setup() function.
  
          The function searches the $SCPATH for libs/<name>.py and runs
          the setup function in that module if found.
  
          Args:
              name (str): Module name
  
          Examples:
              >>> chip.load_lib('nangate45')
              Loads the 'nangate45' library
  
          """
  
          func = self.find_function(name, 'setup', 'libs')
          if func is not None:
              self.logger.info(f"Loading library '{name}'")
              self._loaded_modules['libs'].append(name)
              func(self)
          else:
              self.logger.error(f'Library module {name} not found in $SCPATH or siliconcompiler/libs/.')
              raise SiliconCompilerError(f'Library module {name} not found in $SCPATH or siliconcompiler/libs/.')
  
      ##########################################################################
      def load_checklist(self, name):
          """
          Loads a checklist module and runs the setup() function.
  
          The function searches the $SCPATH for checklist/<name>/<name>.py and runs
          the setup function in that module if found.
  
          Args:
              name (str): Module name
  
          Examples:
              >>> chip.load_checklist('oh_tapeout')
              Loads the 'oh_tapeout' checklist
  
          """
  
          func = self.find_function(name, 'setup', 'checklists')
          if func is not None:
              self.logger.info(f"Loading checklist '{name}'")
              self._loaded_modules['checklists'].append(name)
              func(self)
          else:
              self.logger.error(f'Checklist module {name} not found in $SCPATH or siliconcompiler/checklists/.')
              raise SiliconCompilerError(f'Checklist module {name} not found in $SCPATH or siliconcompiler/checklists/.')
  
  
      ###########################################################################
      def list_metrics(self):
          '''
          Returns a list of all metrics in the schema.
  
          '''
  
          return self.getkeys('metric','default','default')
  
      ###########################################################################
      def help(self, *keypath):
          """
          Returns a schema parameter description.
  
          Args:
              *keypath(str): Keypath to parameter.
  
          Returns:
              A formatted multi-line help paragraph for the parameter provided.
  
          Examples:
              >>> print(chip.help('asic','diearea'))
              Displays help information about the 'asic, diearea' parameter
  
          """
  
          self.logger.debug('Fetching help for %s', keypath)
  
          #Fetch Values
  
          description = self.get(*keypath, field='shorthelp')
          typestr = self.get(*keypath, field='type')
          switchstr = str(self.get(*keypath, field='switch'))
          defstr = str(self.get(*keypath, field='defvalue'))
          requirement = str(self.get(*keypath, field='require'))
          helpstr = self.get(*keypath, field='help')
          example = self.get(*keypath, field='example')
  
          examplestr = ("\nExamples:    " + example[0] + ''.join(
                       ["\n             " + ex for ex in example[1:]]))
  
          #Removing multiple spaces and newlines
          helpstr = helpstr.rstrip()
          helpstr = helpstr.replace("\n", "")
          helpstr = ' '.join(helpstr.split())
  
          for idx, item in enumerate(example):
              example[idx] = ' '.join(item.split())
              example[idx] = example[idx].replace(", ", ",")
  
          #Wrap text
          para = textwrap.TextWrapper(width=60)
          para_list = para.wrap(text=helpstr)
  
          #Full Doc String
          fullstr = ("-"*80 +
                     "\nDescription: " + description +
                     "\nSwitch:      " + switchstr +
                     "\nType:        " + typestr  +
                     "\nRequirement: " + requirement   +
                     "\nDefault:     " + defstr   +
                     examplestr +
                     "\nHelp:        " + para_list[0] + "\n")
          for line in para_list[1:]:
              fullstr = (fullstr +
                         " "*13 + line.lstrip() + "\n")
  
          return fullstr
  
  
      ###########################################################################
      def valid(self, *args, valid_keypaths=None, quiet=True, default_valid=False):
          """
          Checks validity of a keypath.
  
          Checks the validity of a parameter keypath and returns True if the
          keypath is valid and False if invalid.
  
          Args:
              keypath(list str): Variable length schema key list.
              valid_keypaths (list of list): List of valid keypaths as lists. If
                  None, check against all keypaths in the schema.
              quiet (bool): If True, don't display warnings for invalid keypaths.
  
          Returns:
              Boolean indicating validity of keypath.
  
          Examples:
              >>> check = chip.valid('design')
              Returns True.
              >>> check = chip.valid('blah')
              Returns False.
          """
  
          keypathstr = ','.join(args)
          keylist = list(args)
          if default_valid:
              default = 'default'
          else:
              default = None
  
          if valid_keypaths is None:
              valid_keypaths = self.getkeys()
  
          # Look for a full match with default playing wild card
          for valid_keypath in valid_keypaths:
              if len(keylist) != len(valid_keypath):
                  continue
  
              ok = True
              for i in range(len(keylist)):
                  if valid_keypath[i] not in (keylist[i], default):
                      ok = False
                      break
              if ok:
                  return True
  
          # Match not found
          if not quiet:
              self.logger.warning(f"Keypath [{keypathstr}] is not valid")
          return False
  
      ###########################################################################
      def get(self, *keypath, field='value', job=None, cfg=None):
          """
          Returns a schema parameter field.
  
          Returns a schema parameter field based on the keypath provided in the
          ``*keypath``. See the :ref:`Schema Reference Manual<SiliconCompiler
          Schema>` for documentation of all supported keypaths. The returned type
          is consistent with the type field of the parameter. Fetching parameters
          with empty or undefined value files returns None for scalar types and []
          (empty list) for list types.  Accessing a non-existent keypath produces
          a logger error message and raises the Chip object error flag.
  
          Args:
              keypath(list str): Variable length schema key list.
              field(str): Parameter field to fetch.
              job (str): Jobname to use for dictionary access in place of the
                  current active jobname.
              cfg(dict): Alternate dictionary to access in place of the default
                  chip object schema dictionary.
  
          Returns:
              Value found for the keypath and field provided.
  
          Examples:
              >>> foundry = chip.get('pdk', 'foundry')
              Returns the name of the foundry from the PDK.
  
          """
  
          if cfg is None:
              if job is not None:
                  cfg = self.cfg['history'][job]
              else:
                  cfg = self.cfg
  
          keypathstr = ','.join(keypath)
  
          self.logger.debug(f"Reading from [{keypathstr}]. Field = '{field}'")
          return self._search(cfg, keypathstr, *keypath, field=field, mode='get')
  
      ###########################################################################
      def getkeys(self, *keypath, cfg=None, job=None):
          """
          Returns a list of schema dictionary keys.
  
          Searches the schema for the keypath provided and returns a list of
          keys found, excluding the generic 'default' key. Accessing a
          non-existent keypath produces a logger error message and raises the
          Chip object error flag.
  
          Args:
              keypath (list str): Variable length ordered schema key list
              cfg (dict): Alternate dictionary to access in place of self.cfg
              job (str): Jobname to use for dictionary access in place of the
                  current active jobname.
  
          Returns:
              List of keys found for the keypath provided.
  
          Examples:
              >>> keylist = chip.getkeys('pdk')
              Returns all keys for the 'pdk' keypath.
              >>> keylist = chip.getkeys()
              Returns all list of all keypaths in the schema.
          """
  
          if cfg is None:
              if job is None:
                  cfg = self.cfg
              else:
                  cfg = self.cfg['history'][job]
  
          if len(list(keypath)) > 0:
              keypathstr = ','.join(keypath)
              self.logger.debug('Getting schema parameter keys for: %s', keypathstr)
              keys = list(self._search(cfg, keypathstr, *keypath, mode='getkeys'))
              if 'default' in keys:
                  keys.remove('default')
          else:
              self.logger.debug('Getting all schema parameter keys.')
              keys = list(self._allkeys(cfg))
  
          return keys
  
      ###########################################################################
      def getdict(self, *keypath, cfg=None):
          """
          Returns a schema dictionary.
  
          Searches the schema for the keypath provided and returns a complete
          dictionary. Accessing a non-existent keypath produces a logger error
          message and raises the Chip object error flag.
  
          Args:
              keypath(list str): Variable length ordered schema key list
              cfg(dict): Alternate dictionary to access in place of self.cfg
  
          Returns:
              A schema dictionary
  
          Examples:
              >>> pdk = chip.getdict('pdk')
              Returns the complete dictionary found for the keypath 'pdk'
          """
  
          if cfg is None:
              cfg = self.cfg
  
          if len(list(keypath)) > 0:
              keypathstr = ','.join(keypath)
              self.logger.debug('Getting cfg for: %s', keypathstr)
              localcfg = self._search(cfg, keypathstr, *keypath, mode='getcfg')
  
          return copy.deepcopy(localcfg)
  
      ###########################################################################
      def set(self, *args, field='value', clobber=True, cfg=None):
          '''
          Sets a schema parameter field.
  
          Sets a schema parameter field based on the keypath and value provided in
          the ``*args``. See the :ref:`Schema Reference Manual<SiliconCompiler
          Schema>` for documentation of all supported keypaths. New schema
          dictionaries are automatically created for keypaths that overlap with
          'default' dictionaries. The write action is ignored if the parameter
          value is non-empty and the clobber option is set to False.
  
          The value provided must agree with the dictionary parameter 'type'.
          Accessing a non-existent keypath or providing a value that disagrees
          with the parameter type produces a logger error message and raises the
          Chip object error flag.
  
          Args:
              args (list): Parameter keypath followed by a value to set.
              field (str): Parameter field to set.
              clobber (bool): Existing value is overwritten if True.
              cfg(dict): Alternate dictionary to access in place of self.cfg
  
          Examples:
              >>> chip.set('design', 'top')
              Sets the name of the design to 'top'
          '''
  
          if cfg is None:
              cfg = self.cfg
  
          # Verify that all keys are strings
          for key in args[:-1]:
              if not isinstance(key,str):
                  self.logger.error(f"Key [{key}] is not a string [{args}]")
  
          keypathstr = ','.join(args[:-1])
          all_args = list(args)
  
          # Special case to ensure loglevel is updated ASAP
          if len(args) == 3 and args[1] == 'loglevel' and field == 'value':
              self.logger.setLevel(args[2])
  
          self.logger.debug(f"Setting [{keypathstr}] to {args[-1]}")
          return self._search(cfg, keypathstr, *all_args, field=field, mode='set', clobber=clobber)
  
      ###########################################################################
      def add(self, *args, cfg=None, field='value'):
          '''
          Adds item(s) to a schema parameter list.
  
          Adds item(s) to schema parameter list based on the keypath and value
          provided in the ``*args``.  See the :ref:`Schema Reference
          Manual<SiliconCompiler Schema>` for documentation of all supported
          keypaths. New schema dictionaries are automatically created for keypaths
          that overlap with 'default' dictionaries.
  
          The value provided must agree with the dictionary parameter 'type'.
          Accessing a non-existent keypath, providing a value that disagrees
          with the parameter type, or using add with a scalar parameter produces
          a logger error message and raises the Chip object error flag.
  
          Args:
              args (list): Parameter keypath followed by a value to add.
              cfg(dict): Alternate dictionary to access in place of self.cfg
              field (str): Parameter field to set.
  
          Examples:
              >>> chip.add('source', 'hello.v')
              Adds the file 'hello.v' to the list of sources.
          '''
  
          if cfg is None:
              cfg = self.cfg
  
          # Verify that all keys are strings
          for key in args[:-1]:
              if not isinstance(key,str):
                  self.logger.error(f"Key [{key}] is not a string [{args}]")
  
          keypathstr = ','.join(args[:-1])
          all_args = list(args)
  
          self.logger.debug(f'Appending value {args[-1]} to [{keypathstr}]')
          return self._search(cfg, keypathstr, *all_args, field=field, mode='add')
  
  
      ###########################################################################
      def _allkeys(self, cfg, keys=None, keylist=None):
          '''
          Returns list of all keypaths in the schema.
          '''
  
          if keys is None:
              keylist = []
              keys = []
          for k in cfg:
              newkeys = keys.copy()
              newkeys.append(k)
              if 'defvalue' in cfg[k]:
                  keylist.append(newkeys)
              else:
                  self._allkeys(cfg[k], keys=newkeys, keylist=keylist)
          return keylist
  
      ###########################################################################
      def _search(self, cfg, keypath, *args, field='value', mode='get', clobber=True):
          '''
          Internal recursive function that searches the Chip schema for a
          match to the combination of *args and fields supplied. The function is
          used to set and get data within the dictionary.
  
          Args:
              cfg(dict): The cfg schema to search
              keypath (str): Concatenated keypath used for error logging.
              args (str): Keypath/value variable list used for access
              field(str): Leaf cell field to access.
              mode(str): Action (set/get/add/getkeys/getkeys)
              clobber(bool): Specifies to clobber (for set action)
  
          '''
  
          all_args = list(args)
          param = all_args[0]
          val = all_args[-1]
          empty = [None, 'null', [], 'false']
  
          #set/add leaf cell (all_args=(param,val))
          if (mode in ('set', 'add')) & (len(all_args) == 2):
              # clean error if key not found
              if (not param in cfg) & (not 'default' in cfg):
                  self.logger.error(f"Set/Add keypath [{keypath}] does not exist.")
                  self.error = 1
              else:
                  # making an 'instance' of default if not found
                  if (not param in cfg) & ('default' in cfg):
                      cfg[param] = copy.deepcopy(cfg['default'])
                  list_type =bool(re.match(r'\[', cfg[param]['type']))
                  # checking for illegal fields
                  if not field in cfg[param] and (field != 'value'):
                      self.logger.error(f"Field '{field}' for keypath [{keypath}]' is not a valid field.")
                      self.error = 1
                  # check legality of value
                  if field == 'value':
                      (type_ok,type_error) = self._typecheck(cfg[param], param, val)
                      if not type_ok:
                          self.logger.error("%s", type_error)
                          self.error = 1
                  # converting python True/False to lower case string
                  if (field == 'value') and (cfg[param]['type'] == 'bool'):
                      if val == True:
                          val = "true"
                      elif val == False:
                          val = "false"
                  # checking if value has been set
                  # TODO: fix clobber!!
                  selval = cfg[param]['value']
                  # updating values
                  if cfg[param]['lock'] == "true":
                      self.logger.debug("Ignoring {mode}{} to [{keypath}]. Lock bit is set.")
                  elif (mode == 'set'):
                      if (field != 'value') or (selval in empty) or clobber:
                          if field in ('copy', 'lock'):
                              # boolean fields
                              if val is True:
                                  cfg[param][field] = "true"
                              elif val is False:
                                  cfg[param][field] = "false"
                              else:
                                  self.logger.error(f'{field} must be set to boolean.')
                                  self.error = 1
                          elif field in ('hashalgo', 'scope', 'require', 'type', 'unit',
                                         'shorthelp', 'notes', 'switch', 'help'):
                              # awlays string scalars
                              cfg[param][field] = val
                          elif field in ('example'):
                              # list from default schema (already a list)
                              cfg[param][field] = val
                          elif field in ('signature', 'filehash', 'date', 'author'):
                              # convert to list if appropriate
                              if isinstance(val, list) | (not list_type):
                                  cfg[param][field] = val
                              else:
                                  cfg[param][field] = [val]
                          elif (not list_type) & (val is None):
                              # special case for None
                              cfg[param][field] = None
                          elif (not list_type) & (not isinstance(val, list)):
                              # convert to string for scalar value
                              cfg[param][field] = str(val)
                          elif list_type & (not isinstance(val, list)):
                              # convert to string for list value
                              cfg[param][field] = [str(val)]
                          elif list_type & isinstance(val, list):
                              # converting tuples to strings
                              if re.search(r'\(', cfg[param]['type']):
                                  cfg[param][field] = list(map(str,val))
                              else:
                                  cfg[param][field] = val
                          else:
                              self.logger.error(f"Assigning list to scalar for [{keypath}]")
                              self.error = 1
                      else:
                          self.logger.debug(f"Ignoring set() to [{keypath}], value already set. Use clobber=true to override.")
                  elif (mode == 'add'):
                      if field in ('filehash', 'date', 'author', 'signature'):
                          cfg[param][field].append(str(val))
                      elif field in ('copy', 'lock'):
                          self.logger.error(f"Illegal use of add() for scalar field {field}.")
                          self.error = 1
                      elif list_type & (not isinstance(val, list)):
                          cfg[param][field].append(str(val))
                      elif list_type & isinstance(val, list):
                          cfg[param][field].extend(val)
                      else:
                          self.logger.error(f"Illegal use of add() for scalar parameter [{keypath}].")
                          self.error = 1
                  return cfg[param][field]
          #get leaf cell (all_args=param)
          elif len(all_args) == 1:
              if not param in cfg:
                  self.error = 1
                  self.logger.error(f"Get keypath [{keypath}] does not exist.")
              elif mode == 'getcfg':
                  return cfg[param]
              elif mode == 'getkeys':
                  return cfg[param].keys()
              else:
                  if not (field in cfg[param]) and (field!='value'):
                      self.error = 1
                      self.logger.error(f"Field '{field}' not found for keypath [{keypath}]")
                  elif field == 'value':
                      #Select default if no value has been set
                      if field not in cfg[param]:
                          selval = cfg[param]['defvalue']
                      else:
                          selval =  cfg[param]['value']
                      #check for list
                      if bool(re.match(r'\[', cfg[param]['type'])):
                          sctype = re.sub(r'[\[\]]', '', cfg[param]['type'])
                          return_list = []
                          if selval is None:
                              return None
                          for item in selval:
                              if sctype == 'int':
                                  return_list.append(int(item))
                              elif sctype == 'float':
                                  return_list.append(float(item))
                              elif sctype.startswith('(str,'):
                                  if isinstance(item,tuple):
                                      return_list.append(item)
                                  else:
                                      tuplestr = re.sub(r'[\(\)\'\s]','',item)
                                      return_list.append(tuple(tuplestr.split(',')))
                              elif sctype.startswith('(float,'):
                                  if isinstance(item,tuple):
                                      return_list.append(item)
                                  else:
                                      tuplestr = re.sub(r'[\(\)\s]','',item)
                                      return_list.append(tuple(map(float, tuplestr.split(','))))
                              else:
                                  return_list.append(item)
                          return return_list
                      else:
                          if selval is None:
                              # Unset scalar of any type
                              scalar = None
                          elif cfg[param]['type'] == "int":
                              #print(selval, type(selval))
                              scalar = int(float(selval))
                          elif cfg[param]['type'] == "float":
                              scalar = float(selval)
                          elif cfg[param]['type'] == "bool":
                              scalar = (selval == 'true')
                          elif re.match(r'\(', cfg[param]['type']):
                              tuplestr = re.sub(r'[\(\)\s]','',selval)
                              scalar = tuple(map(float, tuplestr.split(',')))
                          else:
                              scalar = selval
                          return scalar
                  #all non-value fields are strings (or lists of strings)
                  else:
                      if cfg[param][field] == 'true':
                          return True
                      elif cfg[param][field] == 'false':
                          return False
                      else:
                          return cfg[param][field]
          #if not leaf cell descend tree
          else:
              ##copying in default tree for dynamic trees
              if not param in cfg and 'default' in cfg:
                  cfg[param] = copy.deepcopy(cfg['default'])
              elif not param in cfg:
                  self.error = 1
                  self.logger.error(f"Get keypath [{keypath}] does not exist.")
                  return None
              all_args.pop(0)
              return self._search(cfg[param], keypath, *all_args, field=field, mode=mode, clobber=clobber)
  
      ###########################################################################
      def _prune(self, cfg, top=True, keeplists=False):
          '''
          Internal recursive function that creates a local copy of the Chip
          schema (cfg) with only essential non-empty parameters retained.
  
          '''
  
          # create a local copy of dict
          if top:
              localcfg = copy.deepcopy(cfg)
          else:
              localcfg = cfg
  
          #10 should be enough for anyone...
          maxdepth = 10
          i = 0
  
          #Prune when the default & value are set to the following
          if keeplists:
              empty = ("null", None)
          else:
              empty = ("null", None, [])
  
          # When at top of tree loop maxdepth times to make sure all stale
          # branches have been removed, not elegant, but stupid-simple
          # "good enough"
          while i < maxdepth:
              #Loop through all keys starting at the top
              for k in list(localcfg.keys()):
                  #removing all default/template keys
                  # reached a default subgraph, delete it
                  if k == 'default':
                      del localcfg[k]
                  # reached leaf-cell
                  elif 'help' in localcfg[k].keys():
                      del localcfg[k]['help']
                  elif 'example' in localcfg[k].keys():
                      del localcfg[k]['example']
                  elif 'defvalue' in localcfg[k].keys():
                      if localcfg[k]['defvalue'] in empty:
                          if 'value' in localcfg[k].keys():
                              if localcfg[k]['value'] in empty:
                                  del localcfg[k]
                          else:
                              del localcfg[k]
                  #removing stale branches
                  elif not localcfg[k]:
                      localcfg.pop(k)
                  #keep traversing tree
                  else:
                      self._prune(cfg=localcfg[k], top=False, keeplists=keeplists)
              if top:
                  i += 1
              else:
                  break
  
          return localcfg
  
      ###########################################################################
      def _find_sc_file(self, filename, missing_ok=False):
          """
          Returns the absolute path for the filename provided.
  
          Searches the SC root directory and the 'scpath' parameter for the
          filename provided and returns the absolute path. If no valid absolute
          path is found during the search, None is returned.
  
          Shell variables ('$' followed by strings consisting of numbers,
          underscores, and digits) are replaced with the variable value.
  
          Args:
              filename (str): Relative or absolute filename.
  
          Returns:
              Returns absolute path of 'filename' if found, otherwise returns
              None.
  
          Examples:
              >>> chip._find_sc_file('flows/asicflow.py')
             Returns the absolute path based on the sc installation directory.
  
          """
  
          # Replacing environment variables
          filename = self._resolve_env_vars(filename)
  
          # If we have a path relative to our cwd or an abs path, pass-through here
          if os.path.exists(os.path.abspath(filename)):
              return os.path.abspath(filename)
  
          # Otherwise, search relative to scpaths
          scpaths = [self.scroot, self.cwd]
          scpaths.extend(self.get('option', 'scpath'))
          if 'SCPATH' in os.environ:
              scpaths.extend(os.environ['SCPATH'].split(os.pathsep))
  
          searchdirs = ', '.join(scpaths)
          self.logger.debug(f"Searching for file {filename} in {searchdirs}")
  
          result = None
          for searchdir in scpaths:
              if not os.path.isabs(searchdir):
                  searchdir = os.path.join(self.cwd, searchdir)
  
              abspath = os.path.abspath(os.path.join(searchdir, filename))
              if os.path.exists(abspath):
                  result = abspath
                  break
  
          if result is None and not missing_ok:
              self.error = 1
              self.logger.error(f"File {filename} was not found")
  
          return result
  
      ###########################################################################
      def find_files(self, *keypath, cfg=None, missing_ok=False, job=None):
          """
          Returns absolute paths to files or directories based on the keypath
          provided.
  
          By default, this function first checks if the keypath provided has its
          `copy` parameter set to True. If so, it returns paths to the files in
          the build directory. Otherwise, it resolves these files based on the
          current working directory and SC path.
  
          The keypath provided must point to a schema parameter of type file, dir,
          or lists of either. Otherwise, it will trigger an error.
  
          Args:
              keypath (list str): Variable length schema key list.
              cfg (dict): Alternate dictionary to access in place of the default
                  chip object schema dictionary.
              missing_ok (bool): If True, silently return None when files aren't
                  found. If False, print an error and set the error flag.
              job (str): Jobname to use for dictionary access in place of the
                  current active jobname.
  
          Returns:
              If keys points to a scalar entry, returns an absolute path to that
              file/directory, or None if not found. It keys points to a list
              entry, returns a list of either the absolute paths or None for each
              entry, depending on whether it is found.
  
          Examples:
              >>> chip.find_files('source')
              Returns a list of absolute paths to source files, as specified in
              the schema.
  
          """
          if cfg is None:
              cfg = self.cfg
  
          copyall = self.get('option', 'copyall', cfg=cfg, job=job)
          paramtype = self.get(*keypath, field='type', cfg=cfg, job=job)
  
          if 'file' in paramtype:
              copy = self.get(*keypath, field='copy', cfg=cfg, job=job)
          else:
              copy = False
  
          if 'file' not in paramtype and 'dir' not in paramtype:
              self.logger.error('Can only call find_files on file or dir types')
              self.error = 1
              return None
  
          is_list = bool(re.match(r'\[', paramtype))
  
          paths = self.get(*keypath, cfg=cfg, job=job)
          # Convert to list if we have scalar
          if not is_list:
              paths = [paths]
  
          result = []
  
          # Special cases for various ['eda', ...] files that may be implicitly
          # under the workdir (or refdir in the case of scripts).
          # TODO: it may be cleaner to have a file resolution scope flag in schema
          # (e.g. 'scpath', 'workdir', 'refdir'), rather than harcoding special
          # cases.
          if keypath[0] == 'tool' and keypath[2] in ('input', 'output', 'report'):
              step = keypath[3]
              index = keypath[4]
              if keypath[2] == 'report':
                  io = ""
              else:
                  io = keypath[2] + 's'
              iodir = os.path.join(self._getworkdir(jobname=job, step=step, index=index), io)
              for path in paths:
                  abspath = os.path.join(iodir, path)
                  if os.path.isfile(abspath):
                      result.append(abspath)
              return result
          elif keypath[0] == 'tool' and keypath[2] == 'script':
              tool = keypath[1]
              step = keypath[3]
              index = keypath[4]
              refdirs = self.find_files('tool', tool, 'refdir', step, index)
              for path in paths:
                  for refdir in refdirs:
                      abspath = os.path.join(refdir, path)
                      if os.path.isfile(abspath):
                          result.append(abspath)
                          break
  
              return result
  
          for path in paths:
              if (copyall or copy) and ('file' in paramtype):
                  name = self._get_imported_filename(path)
                  abspath = os.path.join(self._getworkdir(jobname=job, step='import'), 'outputs', name)
                  if os.path.isfile(abspath):
                      # if copy is True and file is found in import outputs,
                      # continue. Otherwise, fall through to _find_sc_file (the
                      # file may not have been gathered in imports yet)
                      result.append(abspath)
                      continue
              result.append(self._find_sc_file(path, missing_ok=missing_ok))
          # Convert back to scalar if that was original type
          if not is_list:
              return result[0]
  
          return result
  
      ###########################################################################
      def find_result(self, filetype, step, jobname=None, index='0'):
          """
          Returns the absolute path of a compilation result.
  
          Utility function that returns the absolute path to a results
          file based on the provided arguments. The result directory
          structure is:
  
          <dir>/<design>/<jobname>/<step>/<index>/outputs/<design>.filetype
  
          Args:
              filetype (str): File extension (.v, .def, etc)
              step (str): Task step name ('syn', 'place', etc)
              jobname (str): Jobid directory name
              index (str): Task index
  
          Returns:
              Returns absolute path to file.
  
          Examples:
              >>> manifest_filepath = chip.find_result('.vg', 'syn')
             Returns the absolute path to the manifest.
          """
          if jobname is None:
              jobname = self.get('option', 'jobname')
  
          workdir = self._getworkdir(jobname, step, index)
          design = self.get('design')
          filename = f"{workdir}/outputs/{design}.{filetype}"
  
          self.logger.debug("Finding result %s", filename)
  
          if os.path.isfile(filename):
              return filename
          else:
              return None
  
      ###########################################################################
      def _abspath(self, cfg):
          '''
          Internal function that goes through provided dictionary and resolves all
          relative paths where required.
          '''
  
          for keypath in self.getkeys(cfg=cfg):
              paramtype = self.get(*keypath, cfg=cfg, field='type')
              value = self.get(*keypath, cfg=cfg)
              if value:
                  #only do something if type is file or dir
                  if 'file' in paramtype or 'dir' in paramtype:
                      abspaths = self.find_files(*keypath, cfg=cfg, missing_ok=True)
                      self.set(*keypath, abspaths, cfg=cfg)
  
      ###########################################################################
      def _print_csv(self, cfg, fout):
          csvwriter = csv.writer(fout)
          csvwriter.writerow(['Keypath', 'Value'])
  
          allkeys = self.getkeys(cfg=cfg)
          for key in allkeys:
              keypath = ','.join(key)
              value = self.get(*key, cfg=cfg)
              if isinstance(value,list):
                  for item in value:
                      csvwriter.writerow([keypath, item])
              else:
                  csvwriter.writerow([keypath, value])
  
      ###########################################################################
      def _escape_val_tcl(self, val, typestr):
          '''Recursive helper function for converting Python values to safe TCL
          values, based on the SC type string.'''
          if val is None:
              return ''
          elif typestr.startswith('('):
              # Recurse into each item of tuple
              subtypes = typestr.strip('()').split(',')
              valstr = ' '.join(self._escape_val_tcl(v, subtype.strip())
                                for v, subtype in zip(val, subtypes))
              return f'[list {valstr}]'
          elif typestr.startswith('['):
              # Recurse into each item of list
              subtype = typestr.strip('[]')
              valstr = ' '.join(self._escape_val_tcl(v, subtype) for v in val)
              return f'[list {valstr}]'
          elif typestr == 'bool':
              return 'true' if val else 'false'
          elif typestr == 'str':
              # Escape string by surrounding it with "" and escaping the few
              # special characters that still get considered inside "". We don't
              # use {}, since this requires adding permanent backslashes to any
              # curly braces inside the string.
              # Source: https://www.tcl.tk/man/tcl8.4/TclCmd/Tcl.html (section [4] on)
              escaped_val = (val.replace('\\', '\\\\') # escape '\' to avoid backslash substition (do this first, since other replaces insert '\')
                                .replace('[', '\\[')   # escape '[' to avoid command substition
                                .replace('$', '\\$')   # escape '$' to avoid variable substition
                                .replace('"', '\\"'))  # escape '"' to avoid string terminating early
              return '"' + escaped_val + '"'
          elif typestr in ('file', 'dir'):
              # Replace $VAR with $env(VAR) for tcl
              val = re.sub(r'\$(\w+)', r'$env(\1)', val)
              # Same escapes as applied to string, minus $ (since we want to resolve env vars).
              escaped_val = (val.replace('\\', '\\\\') # escape '\' to avoid backslash substition (do this first, since other replaces insert '\')
                                .replace('[', '\\[')   # escape '[' to avoid command substition
                                .replace('"', '\\"'))  # escape '"' to avoid string terminating early
              return '"' +  escaped_val + '"'
          else:
              # floats/ints just become strings
              return str(val)
  
      ###########################################################################
      def _print_tcl(self, cfg, fout=None, prefix=""):
          '''
          Prints out schema as TCL dictionary
          '''
  
          fout.write("#############################################")
          fout.write("#!!!! AUTO-GENERATED FILE. DO NOT EDIT!!!!!!")
          fout.write("#############################################\n")
  
          allkeys = self.getkeys(cfg=cfg)
  
          for key in allkeys:
              typestr = self.get(*key, cfg=cfg, field='type')
              value = self.get(*key, cfg=cfg)
  
              #create a TCL dict
              keystr = ' '.join(key)
  
              valstr = self._escape_val_tcl(value, typestr)
  
              if not (typestr.startswith('[') or typestr.startswith('(')):
                  # treat scalars as lists as well
                  valstr = f'[list {valstr}]'
  
              outstr = f"{prefix} {keystr} {valstr}\n"
  
              #print out all non default values
              if 'default' not in key:
                  fout.write(outstr)
  
      ###########################################################################
  
      def merge_manifest(self, cfg, job=None, clobber=True, clear=True, check=False):
          """
          Merges an external manifest with the current compilation manifest.
  
          All value fields in the provided schema dictionary are merged into the
          current chip object. Dictionaries with non-existent keypath produces a
          logger error message and raises the Chip object error flag.
  
          Args:
              job (str): Specifies non-default job to merge into
              clear (bool): If True, disables append operations for list type
              clobber (bool): If True, overwrites existing parameter value
              check (bool): If True, checks the validity of each key
              partial (bool): If True, perform a partial merge, only merging
                  keypaths that may have been updated during run().
  
          Examples:
              >>> chip.merge_manifest('my.pkg.json')
             Merges all parameters in my.pk.json into the Chip object
  
          """
          self._merge_manifest(cfg, job, clobber, clear, check)
  
      def _key_may_be_updated(self, keypath):
          '''Helper that returns whether `keypath` can be updated mid-run.'''
          # TODO: cleaner way to manage this?
          if keypath[0] in ('metric', 'record'):
              return True
          if keypath[0] == 'flowgraph' and keypath[4] in ('select', 'status'):
              return True
          return False
  
      ###########################################################################
      def _merge_manifest(self, cfg, job=None, clobber=True, clear=True, check=False, partial=False):
          """
          Internal merge_manifest() implementation with `partial` arg.
  
          partial (bool): If True, perform a partial merge, only merging keypaths
          that may have been updated during run().
          """
          if job is not None:
              # fill ith default schema before populating
              self.cfg['history'][job] = schema_cfg()
              dst = self.cfg['history'][job]
          else:
              dst = self.cfg
  
          for keylist in self.getkeys(cfg=cfg):
              if partial and not self._key_may_be_updated(keylist):
                  continue
              if keylist[0] in ('history', 'library'):
                  continue
              #only read in valid keypaths without 'default'
              key_valid = True
              if check:
                  key_valid = self.valid(*keylist, quiet=False, default_valid=True)
              if key_valid and 'default' not in keylist:
                  # update value, handling scalars vs. lists
                  typestr = self.get(*keylist, cfg=cfg, field='type')
                  val = self.get(*keylist, cfg=cfg)
                  arg = keylist.copy()
                  arg.append(val)
                  if bool(re.match(r'\[', typestr)) & bool(not clear):
                      self.add(*arg, cfg=dst)
                  else:
                      self.set(*arg, cfg=dst, clobber=clobber)
  
                  # update other fields that a user might modify
                  for field in self.getdict(*keylist, cfg=cfg).keys():
                      if field in ('value', 'switch', 'type', 'require', 'defvalue',
                                   'shorthelp', 'example', 'help'):
                          # skip these fields (value handled above, others are static)
                          continue
                      v = self.get(*keylist, cfg=cfg, field=field)
                      self.set(*keylist, v, cfg=dst, field=field)
  
      ###########################################################################
      def _keypath_empty(self, key):
          '''
          Utility function to check key for an empty list.
          '''
  
          emptylist = ("null", None, [])
  
          value = self.get(*key)
          defvalue = self.get(*key, field='defvalue')
          value_empty = (defvalue in emptylist) and (value in emptylist)
  
          return value_empty
  
      ###########################################################################
      def _check_files(self):
          allowed_paths = [os.path.join(self.cwd, self.get('option', 'builddir'))]
          allowed_paths.extend(os.environ['SC_VALID_PATHS'].split(os.pathsep))
  
          for keypath in self.getkeys():
              if 'default' in keypath:
                  continue
  
              paramtype = self.get(*keypath, field='type')
              #only do something if type is file or dir
              if ('history' not in keypath and 'library' not in keypath) and ('file' in paramtype or 'dir' in paramtype):
  
                  if self.get(*keypath) is None:
                      # skip unset values (some directories are None by default)
                      continue
  
                  abspaths = self.find_files(*keypath, missing_ok=True)
                  if not isinstance(abspaths, list):
                      abspaths = [abspaths]
  
                  for abspath in abspaths:
                      ok = False
  
                      if abspath is not None:
                          for allowed_path in allowed_paths:
                              if os.path.commonpath([abspath, allowed_path]) == allowed_path:
                                  ok = True
                                  continue
  
                      if not ok:
                          self.logger.error(f'Keypath {keypath} contains path(s) '
                              'that do not exist or resolve to files outside of '
                              'allowed directories.')
                          return False
  
          return True
  
      ###########################################################################
      def check_filepaths(self):
          '''
          Verifies that paths to all files in manifest are valid.
          '''
  
          allkeys = self.getkeys()
          for keypath in allkeys:
              allpaths = []
              paramtype = self.get(*keypath, field='type')
              if 'file' in paramtype or 'dir' in paramtype:
                  if 'dir' not in keypath and self.get(*keypath):
                      allpaths = list(self.get(*keypath))
                  for path in allpaths:
                      #check for env var
                      m = re.match(r'\$(\w+)(.*)', path)
                      if m:
                          prefix_path = os.environ[m.group(1)]
                          path = prefix_path + m.group(2)
                      file_error = 'file' in paramtype and not os.path.isfile(path)
                      dir_error = 'dir' in paramtype and not os.path.isdir(path)
                      if file_error or dir_error:
                          self.logger.error(f"Paramater {keypath} path {path} is invalid")
                          self.error = 1
  
      ###########################################################################
      def _check_manifest_dynamic(self, step, index):
          '''Runtime checks called from _runtask().
  
          - Make sure expected inputs exist.
          - Make sure all required filepaths resolve correctly.
          '''
          flow = self.get('option', 'flow')
          tool = self.get('flowgraph', flow, step, index, 'tool')
          if self.valid('tool', tool, 'input', step, index):
              required_inputs = self.get('tool', tool, 'input', step, index)
          else:
              required_inputs = []
          input_dir = os.path.join(self._getworkdir(step=step, index=index), 'inputs')
          for filename in required_inputs:
              path = os.path.join(input_dir, filename)
              if not os.path.isfile(path):
                  self.logger.error(f'Required input {filename} not received for {step}{index}.')
                  self.error = 1
  
          if (not tool in self.builtin) and self.valid('tool', tool, 'require', step, index):
              all_required = self.get('tool', tool, 'require', step, index)
              for item in all_required:
                  keypath = item.split(',')
                  paramtype = self.get(*keypath, field='type')
                  if ('file' in paramtype) or ('dir' in paramtype):
                      abspath = self.find_files(*keypath)
                      if abspath is None or (isinstance(abspath, list) and None in abspath):
                          self.logger.error(f"Required file keypath {keypath} can't be resolved.")
                          self.error = 1
  
          # Need to run this check here since file resolution can change in
          # _runtask().
          if 'SC_VALID_PATHS' in os.environ:
              if not self._check_files():
                  self.error = 1
  
          return self.error
  
      ###########################################################################
      def check_manifest(self):
          '''
          Verifies the integrity of the pre-run compilation manifest.
  
          Checks the validity of the current schema manifest in
          memory to ensure that the design has been properly set up prior
          to running compilation. The function is called inside the run()
          function but can also be called separately. Checks performed by the
          check_manifest() function include:
  
          * Has a flowgraph been defined?
          * Does the manifest satisfy the schema requirement field settings?
          * Are all flowgraph input names legal step/index pairs?
          * Are the tool parameter setting requirements met?
  
          Returns:
              Returns True if the manifest is valid, else returns False.
  
          Examples:
              >>> manifest_ok = chip.check_manifest()
              Returns True of the Chip object dictionary checks out.
  
          '''
          # Dynamic checks
          # We only perform these if arg, step and arg, index are set.
          # We don't check inputs for skip all
          # TODO: Need to add skip step
  
          cur_step = self.get('arg', 'step')
          cur_index = self.get('arg', 'index')
          if cur_step and cur_index and not self.get('option', 'skipall'):
              return self._check_manifest_dynamic(cur_step, cur_index)
  
          design = self.get('design')
          flow = self.get('option', 'flow')
          jobname = self.get('option', 'jobname')
          steplist = self.get('option', 'steplist')
          if not steplist:
              steplist = self.list_steps()
  
          #1. Checking that flowgraph and steplist are legal
          if flow not in self.getkeys('flowgraph'):
              self.error = 1
              self.logger.error(f"flowgraph {flow} not defined.")
          legal_steps = self.getkeys('flowgraph',flow)
  
          if 'import' not in legal_steps:
              self.error = 1
              self.logger.error("Flowgraph doesn't contain import step.")
  
          indexlist = {}
          #TODO: refactor
          for step in steplist:
              if self.get('option', 'indexlist'):
                  indexlist[step] = self.get('option', 'indexlist')
              else:
                  indexlist[step] = self.getkeys('flowgraph', flow, step)
  
          for step in steplist:
              for index in indexlist[step]:
                  in_job = None
                  if (step in self.getkeys('option', 'jobinput') and
                      index in self.getkeys('option', 'jobinput', step)):
                      in_job = self.get('option', 'jobinput', step, index)
  
                  for in_step, in_index in self.get('flowgraph', flow, step, index, 'input'):
                      if in_job is not None:
                          workdir = self._getworkdir(jobname=in_job, step=in_step, index=in_index)
                          cfg = os.path.join(workdir, 'outputs', f'{design}.pkg.json')
                          if not os.path.isfile(cfg):
                              self.logger.error(f'{step}{index} relies on {in_step}{in_index} from job {in_job}, '
                                  'but this task has not been run.')
                              self.error = 1
                          continue
                      if in_step in steplist and in_index in indexlist[in_step]:
                          # we're gonna run this step, OK
                          continue
                      if self.get('flowgraph', flow, in_step, in_index, 'status') == TaskStatus.SUCCESS:
                          # this task has already completed successfully, OK
                          continue
                      self.logger.error(f'{step}{index} relies on {in_step}{in_index}, '
                          'but this task has not been run and is not in the current steplist.')
                      self.error = 1
  
          #2. Check libary names
          for item in self.get('asic', 'logiclib'):
              if item not in self.getkeys('library'):
                  self.error = 1
                  self.logger.error(f"Target library {item} not found.")
  
          #3. Check requirements list
          allkeys = self.getkeys()
          for key in allkeys:
              keypath = ",".join(key)
              if 'default' not in key and 'history' not in key and 'library' not in key:
                  key_empty = self._keypath_empty(key)
                  requirement = self.get(*key, field='require')
                  if key_empty and (str(requirement) == 'all'):
                      self.error = 1
                      self.logger.error(f"Global requirement missing for [{keypath}].")
                  elif key_empty and (str(requirement) == self.get('option', 'mode')):
                      self.error = 1
                      self.logger.error(f"Mode requirement missing for [{keypath}].")
  
          #4. Check per tool parameter requirements (when tool exists)
          for step in steplist:
              for index in self.getkeys('flowgraph', flow, step):
                  tool = self.get('flowgraph', flow, step, index, 'tool')
                  if (tool not in self.builtin) and (tool in self.getkeys('tool')):
                      # checking that requirements are set
                      if self.valid('tool', tool, 'require', step, index):
                          all_required = self.get('tool', tool, 'require', step, index)
                          for item in all_required:
                              keypath = item.split(',')
                              if self._keypath_empty(keypath):
                                  self.error = 1
                                  self.logger.error(f"Value empty for [{keypath}] for {tool}.")
                      if self._keypath_empty(['tool', tool, 'exe']):
                          self.error = 1
                          self.logger.error(f'Executable not specified for tool {tool}')
  
          if 'SC_VALID_PATHS' in os.environ:
              if not self._check_files():
                  self.error = 1
  
          if not self._check_flowgraph_io():
              self.error = 1
  
          return self.error
  
      ###########################################################################
      def _gather_outputs(self, step, index):
          '''Return set of filenames that are guaranteed to be in outputs
          directory after a successful run of step/index.'''
  
          flow = self.get('option', 'flow')
          tool = self.get('flowgraph', flow, step, index, 'tool')
  
          outputs = set()
          if tool in self.builtin:
              in_tasks = self.get('flowgraph', flow, step, index, 'input')
              in_task_outputs = [self._gather_outputs(*task) for task in in_tasks]
  
              if tool in ('minimum', 'maximum'):
                  if len(in_task_outputs) > 0:
                      outputs = in_task_outputs[0].intersection(*in_task_outputs[1:])
              elif tool in ('join', 'nop'):
                  if len(in_task_outputs) > 0:
                      outputs = in_task_outputs[0].union(*in_task_outputs[1:])
              else:
                  # TODO: logic should be added here when mux/verify builtins are implemented.
                  self.logger.error(f'Builtin {tool} not yet implemented')
          else:
              # Not builtin tool
              if self.valid('tool', tool, 'output', step, index):
                  outputs = set(self.get('tool', tool, 'output', step, index))
              else:
                  outputs = set()
  
          if step == 'import':
              imports = {self._get_imported_filename(p) for p in self._collect_paths()}
              outputs.update(imports)
  
          return outputs
  
      ###########################################################################
      def _check_flowgraph_io(self):
          '''Check if flowgraph is valid in terms of input and output files.
  
          Returns True if valid, False otherwise.
          '''
  
          flow = self.get('option', 'flow')
          steplist = self.get('option', 'steplist')
  
          if not steplist:
              steplist = self.list_steps()
  
          if len(steplist) < 2:
              return True
  
          for step in steplist:
              for index in self.getkeys('flowgraph', flow, step):
                  # For each task, check input requirements.
                  tool = self.get('flowgraph', flow, step, index, 'tool')
                  if tool in self.builtin:
                      # We can skip builtins since they don't have any particular
                      # input requirements -- they just pass through what they
                      # receive.
                      continue
  
                  # Get files we receive from input tasks.
                  in_tasks = self.get('flowgraph', flow, step, index, 'input')
                  if len(in_tasks) > 1:
                      self.logger.error(f'Tool task {step}{index} has more than one input task.')
                  elif len(in_tasks) > 0:
                      in_step, in_index = in_tasks[0]
                      if in_step not in steplist:
                          # If we're not running the input step, the required
                          # inputs need to already be copied into the build
                          # directory.
                          jobname = self.get('option', 'jobname')
                          if self.valid('option', 'jobinput', step, index):
                              in_job = self.get('option', 'jobinput', step, index)
                          else:
                              in_job = jobname
                          workdir = self._getworkdir(jobname=in_job, step=in_step, index=in_index)
                          in_step_out_dir = os.path.join(workdir, 'outputs')
                          inputs = set(os.listdir(in_step_out_dir))
                      else:
                          inputs = self._gather_outputs(in_step, in_index)
                  else:
                      inputs = set()
  
                  if self.valid('tool', tool, 'input', step, index):
                      requirements = self.get('tool', tool, 'input', step, index)
                  else:
                      requirements = []
                  for requirement in requirements:
                      if requirement not in inputs:
                          self.logger.error(f'Invalid flow: {step}{index} will '
                              f'not receive required input {requirement}.')
                          return False
  
          return True
  
      ###########################################################################
      def read_manifest(self, filename, job=None, clear=True, clobber=True):
          """
          Reads a manifest from disk and merges it with the current compilation manifest.
  
          The file format read is determined by the filename suffix. Currently
          json (*.json) and yaml(*.yaml) formats are supported.
  
          Args:
              filename (filepath): Path to a manifest file to be loaded.
              job (str): Specifies non-default job to merge into.
              clear (bool): If True, disables append operations for list type.
              clobber (bool): If True, overwrites existing parameter value.
  
          Examples:
              >>> chip.read_manifest('mychip.json')
              Loads the file mychip.json into the current Chip object.
          """
          self._read_manifest(filename, job=job, clear=clear, clobber=clobber)
  
      ###########################################################################
      def _read_manifest(self, filename, job=None, clear=True, clobber=True, partial=False):
          """
          Internal read_manifest() implementation with `partial` arg.
  
          partial (bool): If True, perform a partial merge, only merging keypaths
          that may have been updated during run().
          """
  
          filepath = os.path.abspath(filename)
          self.logger.debug(f"Reading manifest {filepath}")
          if not os.path.isfile(filepath):
              error_message =  f"Manifest file not found {filepath}"
              self.logger.error(error_message)
              raise SiliconCompilerError(error_message)
  
          #Read arguments from file based on file type
  
          if filepath.endswith('.gz'):
              fin = gzip.open(filepath, 'r')
          else:
              fin = open(filepath, 'r')
  
          try:
              if re.search(r'(\.json|\.sup)(\.gz)*$', filepath):
                  localcfg = json.load(fin)
              elif re.search(r'(\.yaml|\.yml)(\.gz)*$', filepath):
                  localcfg = yaml.load(fin, Loader=yaml.SafeLoader)
              else:
                  self.logger.error('File format not recognized %s', filepath)
                  self.error = 1
          finally:
              fin.close()
  
          if self.get('schemaversion') != localcfg['schemaversion']['value']:
              self.logger.warning('Attempting to read manifest with incompatible '
              'schema version into current chip object. Skipping...')
              return
  
          # Merging arguments with the Chip configuration
          self._merge_manifest(localcfg, job=job, clear=clear, clobber=clobber, partial=partial)
  
          # Read history
          if 'history' in localcfg and not partial:
              for historic_job in localcfg['history'].keys():
                  self._merge_manifest(localcfg['history'][historic_job],
                                          job=historic_job,
                                          clear=clear,
                                          clobber=clobber,
                                          partial=False)
  
          if 'library' in localcfg and not partial:
              for libname in localcfg['library'].keys():
                  if libname in self.cfg['library']:
                      # TODO: should we make this a proper merge?
                      self.logger.warning(f'Overwriting existing library {libname} '
                          f'in object with values read from {filename}.')
                  self._import_library(libname, localcfg['library'][libname])
  
      ###########################################################################
      def write_manifest(self, filename, prune=True, abspath=False, job=None):
          '''
          Writes the compilation manifest to a file.
  
          The write file format is determined by the filename suffix. Currently
          json (*.json), yaml (*.yaml), tcl (*.tcl), and (*.csv) formats are
          supported.
  
          Args:
              filename (filepath): Output filepath
              prune (bool): If True, essential non-empty parameters from the
                   the Chip object schema are written to the output file.
              abspath (bool): If set to True, then all schema filepaths
                   are resolved to absolute filepaths.
  
          Examples:
              >>> chip.write_manifest('mydump.json')
              Prunes and dumps the current chip manifest into mydump.json
          '''
  
          filepath = os.path.abspath(filename)
          self.logger.debug('Writing manifest to %s', filepath)
  
          if not os.path.exists(os.path.dirname(filepath)):
              os.makedirs(os.path.dirname(filepath))
  
          if prune:
              self.logger.debug('Pruning dictionary before writing file %s', filepath)
              # Keep empty lists to simplify TCL coding
              if filepath.endswith('.tcl'):
                  keeplists = True
              else:
                  keeplists = False
              cfgcopy = self._prune(self.cfg, keeplists=keeplists)
          else:
              cfgcopy = copy.deepcopy(self.cfg)
  
          # resolve absolute paths
          if abspath:
              self._abspath(cfgcopy)
  
          is_csv = re.search(r'(\.csv)(\.gz)*$', filepath)
  
          # format specific dumping
          if filepath.endswith('.gz'):
              fout = gzip.open(filepath, 'wt', encoding='UTF-8')
          elif is_csv:
              # Files written using csv library should be opened with newline=''
              # https://docs.python.org/3/library/csv.html#id3
              fout = open(filepath, 'w', newline='')
          else:
              fout = open(filepath, 'w')
  
          # format specific printing
          try:
              if re.search(r'(\.json|\.sup)(\.gz)*$', filepath):
                  fout.write(json.dumps(cfgcopy, indent=4, sort_keys=True))
              elif re.search(r'(\.yaml|\.yml)(\.gz)*$', filepath):
                  fout.write(yaml.dump(cfgcopy, Dumper=YamlIndentDumper, default_flow_style=False))
              elif re.search(r'(\.tcl)(\.gz)*$', filepath):
                  self._print_tcl(cfgcopy, prefix="dict set sc_cfg", fout=fout)
              elif is_csv:
                  self._print_csv(cfgcopy, fout=fout)
              else:
                  self.logger.error('File format not recognized %s', filepath)
                  self.error = 1
          finally:
              fout.close()
  
      ###########################################################################
      def check_checklist(self, standard, items=None, check_ok=False):
          '''
          Check items in a checklist.
  
          Checks the status of items in a checklist for the standard provided. If
          a specific list of items is unspecified, all items are checked.
  
          All items have an associated 'task' parameter, which indicates which
          tasks can be used to automatically validate the item. For an item to be
          checked, all tasks must satisfy the item's criteria, unless waivers are
          provided. In addition, that task must have generated EDA report files
          for each metric in the criteria.
  
          For items without an associated task, the only requirement is that at
          least one report has been added to that item.
  
          When 'check_ok' is True, every item must also have its 'ok' parameter
          set to True, indicating that a human has reviewed the item.
  
          Args:
              standard (str): Standard to check.
              items (list of str): Items to check from standard.
              check_ok (bool): Whether to check item 'ok' parameter.
  
          Returns:
              Status of item check.
  
          Examples:
              >>> status = chip.check_checklist('iso9000', 'd000')
              Returns status.
          '''
  
          self.logger.info(f'Checking checklist {standard}')
  
          if items is None:
              items = self.getkeys('checklist', standard)
  
          flow = self.get('option', 'flow')
  
          for item in items:
              all_criteria = self.get('checklist', standard, item, 'criteria')
              for criteria in all_criteria:
                  m = re.match(r'(\w+)([\>\=\<]+)(\w+)', criteria)
                  if not m:
                      self.logger.error(f"Illegal checklist criteria: {criteria}")
                      self.error = 1
                      return False
                  elif m.group(1) not in self.getkeys('metric', 'default', 'default'):
                      self.logger.error(f"Critera must use legal metrics only: {criteria}")
                      self.error = 1
                      return False
  
                  metric = m.group(1)
                  op = m.group(2)
                  goal = float(m.group(3))
  
                  tasks = self.get('checklist', standard, item, 'task')
                  for job, step, index in tasks:
                      # Automated checks
                      flow = self.get('option', 'flow', job=job)
                      tool = self.get('flowgraph', flow, step, index, 'tool', job=job)
  
                      value = self.get('metric', step, index, metric,  job=job)
                      criteria_ok = self._safecompare(value, op, goal)
                      if metric in self.getkeys('checklist', standard, item, 'waiver'):
                          waivers = self.get('checklist', standard, item, 'waiver', metric)
                      else:
                          waivers = []
  
                      criteria_str = f'{metric}{op}{goal}'
                      if not criteria_ok and waivers:
                          self.logger.warning(f'{item} criteria {criteria_str} unmet by task {step}{index}, but found waivers.')
                      elif not criteria_ok:
                          self.logger.error(f'{item} criteria {criteria_str} unmet by task {step}{index}.')
                          self.error = 1
                          return False
  
                      if (step in self.getkeys('tool', tool, 'report', job=job) and
                          index in self.getkeys('tool', tool, 'report', step, job=job) and
                          metric in self.getkeys('tool', tool, 'report', step, index, job=job)):
                          eda_reports = self.find_files('tool', tool, 'report', step, index, metric, job=job)
                      else:
                          eda_reports = None
  
                      if not eda_reports:
                          self.logger.error(f'No EDA reports generated for metric {metric} in task {step}{index}')
                          self.error = 1
                          return False
  
                      for report in eda_reports:
                          if report not in self.get('checklist', standard, item, 'report'):
                              self.add('checklist', standard, item, 'report', report)
  
              if len(self.get('checklist', standard, item, 'report')) == 0:
                  # TODO: validate that report exists?
                  self.logger.error(f'No report documenting item {item}')
                  self.error = 1
                  return False
  
              if check_ok and not self.get('checklist', standard, item, 'ok'):
                  self.logger.error(f"Item {item} 'ok' field not checked")
                  self.error = 1
                  return False
  
          self.logger.info('Check succeeded!')
  
          return True
  
      ###########################################################################
      def read_file(self, filename, step='import', index='0'):
          '''
          Read file defined in schema. (WIP)
          '''
          return(0)
  
      ###########################################################################
      def update(self):
          '''
          Update the chip dependency graph.
  
          1. Finds all packages in the local cache
          2. Fetches all packages in the remote registry
          3. Creates a dependency graph based on current chip dependencies and
             dependencies read from dependency json objects.
          4. If autoinstall is set, copy registry packages to local cache.
          5. Error out if package is not found in local cache or in registry.
          6. Error out if autoinstall is set and registry package is missing.
  
          '''
  
          # schema settings
          design = self.get('design')
          reglist = self.get('option', 'registry')
          auto = self.get('option','autoinstall')
  
          # environment settings
          # Local cache location
          if 'SC_HOME' in os.environ:
              home = os.environ['SC_HOME']
          else:
              home = os.environ['HOME']
  
          cache = os.path.join(home,'.sc','registry')
  
          # Indexing all local cache packages
          local = self._build_index(cache)
          remote = self._build_index(reglist)
  
          # Cycle through current chip dependencies
          deps = {}
          for dep in self.getkeys('package', 'dependency'):
              deps[dep] = self.get('package', 'dependency', dep)
  
          depgraph = self._find_deps(cache, local, remote, design, deps, auto)
  
          # Update dependency graph
          for dep in depgraph:
              self.set('package', 'depgraph', dep, depgraph[dep])
  
          return depgraph
  
      ###########################################################################
      def _build_index(self, dirlist):
          '''
          Build a package index for a registry.
          '''
  
          if not isinstance(dirlist, list):
              dirlist = [dirlist]
  
          index = {}
          for item in dirlist:
              if re.match(r'http', item):
                  #TODO
                  pass
              else:
                  packages = os.listdir(item)
                  for i in packages:
                      versions = os.listdir(os.path.join(item, i))
                      index[i] = {}
                      for j in versions:
                          index[i][j] = item
  
          return index
  
      ###########################################################################
      def _install_package(self, cache, dep, ver, remote):
          '''
          Copies a package from remote to local.
          The remote and local arguments are package indices of format:
          index['dirname']['dep']
          '''
  
          package = f"{dep}-{ver}.sup.gz"
  
          self.logger.info(f"Installing package {package} in {cache}")
  
          # Check that package exists in remote registry
          if dep in remote.keys():
              if ver not in list(remote[dep].keys()):
                  self.logger.error(f"Package {dep}-{ver} not found in registry.")
                  sys.exit()
  
          ifile = os.path.join(remote[dep][ver],dep,ver,package)
          odir = os.path.join(cache,dep,ver)
          ofile = os.path.join(odir,package)
  
          # Install package
          os.makedirs(odir, exist_ok=True)
          shutil.copyfile(ifile, ofile)
  
      ###########################################################################
      def _find_deps(self, cache, local, remote, design, deps, auto, depgraph={}, upstream={}):
          '''
          Recursive function to find and install dependencies.
          '''
  
          # install missing dependencies
          depgraph[design] = []
          for dep in deps.keys():
              #TODO: Proper PEP semver matching
              ver = list(deps[dep])[0]
              depgraph[design].append((dep,ver))
              islocal = False
              if dep in local.keys():
                  if ver in local[dep]:
                      islocal = True
  
              # install and update local index
              if auto and islocal:
                  self.logger.info(f"Found package {dep}-{ver} in cache")
              elif auto and not islocal:
                  self._install_package(cache, dep, ver, remote)
                  local[dep]=ver
  
              # look through dependency package files
              package = os.path.join(cache,dep,ver,f"{dep}-{ver}.sup.gz")
              if not os.path.isfile(package):
                  self.logger.error("Package missing. Try 'autoinstall' or install manually.")
                  sys.exit()
              with gzip.open(package, 'r') as f:
                  localcfg = json.load(f)
  
              # done if no more dependencies
              if 'dependency' in localcfg['package']:
                  subdeps = {}
                  subdesign = localcfg['design']['value']
                  depgraph[subdesign] = []
                  for item in localcfg['package']['dependency'].keys():
                      subver = localcfg['package']['dependency'][item]['value']
                      if (item in upstream) and (upstream[item] == subver):
                          # Circular imports are not supported.
                          raise SiliconCompilerError(f'Cannot process circular import: {dep}-{ver} <---> {item}-{subver}.')
                      subdeps[item] = subver
                      upstream[item] = subver
                      depgraph[subdesign].append((item, subver))
                      self._find_deps(cache, local, remote, subdesign, subdeps, auto, depgraph, upstream)
  
          return depgraph
  
      ###########################################################################
      def import_library(self, lib_chip):
          '''Import a Chip object into current Chip as a library.
  
          Args:
              lib_chip (Chip): An instance of Chip to import.
          '''
          self._import_library(lib_chip.design, lib_chip.cfg)
  
      ###########################################################################
      def _import_library(self, libname, libcfg):
          '''Helper to import library with config 'libconfig' as a library
          'libname' in current Chip object.'''
          self.cfg['library'][libname] = copy.deepcopy(libcfg)
          if 'pdk' in self.cfg['library'][libname]:
              del self.cfg['library'][libname]['pdk']
  
      ###########################################################################
      def write_depgraph(self, filename):
          '''
          Writes the package dependency tree to disk.
  
          Supported graphical render formats include png, svg, gif, pdf and a
          few others. (see https://graphviz.org for more information).
  
          Supported text formats include .md, .rst. (see the Linux 'tree'
          command for more information).
  
          '''
  
          return(0)
  
      ###########################################################################
  
      def write_flowgraph(self, filename, flow=None,
                          fillcolor='#ffffff', fontcolor='#000000',
                          fontsize='14', border=True, landscape=False):
          '''Renders and saves the compilation flowgraph to a file.
  
          The chip object flowgraph is traversed to create a graphviz (\*.dot)
          file comprised of node, edges, and labels. The dot file is a
          graphical representation of the flowgraph useful for validating the
          correctness of the execution flow graph. The dot file is then
          converted to the appropriate picture or drawing format based on the
          filename suffix provided. Supported output render formats include
          png, svg, gif, pdf and a few others. For more information about the
          graphviz project, see see https://graphviz.org/
  
          Args:
              filename (filepath): Output filepath
              flow (str): Name of flowgraph to render
              fillcolor(str): Node fill RGB color hex value
              fontcolor (str): Node font RGB color hex value
              fontsize (str): Node text font size
              border (bool): Enables node border if True
              landscape (bool): Renders graph in landscape layout if True
  
          Examples:
              >>> chip.write_flowgraph('mydump.png')
              Renders the object flowgraph and writes the result to a png file.
          '''
          filepath = os.path.abspath(filename)
          self.logger.debug('Writing flowgraph to file %s', filepath)
          fileroot, ext = os.path.splitext(filepath)
          fileformat = ext.replace(".", "")
  
          if flow is None:
              flow = self.get('option', 'flow')
  
          # controlling border width
          if border:
              penwidth = '1'
          else:
              penwidth = '0'
  
          # controlling graph direction
          if landscape:
              rankdir = 'LR'
          else:
              rankdir = 'TB'
  
          dot = graphviz.Digraph(format=fileformat)
          dot.graph_attr['rankdir'] = rankdir
          dot.attr(bgcolor='transparent')
          for step in self.getkeys('flowgraph',flow):
              irange = 0
              for index in self.getkeys('flowgraph', flow, step):
                  irange = irange +1
              for i in range(irange):
                  index = str(i)
                  node = step+index
                  # create step node
                  tool =  self.get('flowgraph', flow, step, index, 'tool')
                  if tool in self.builtin:
                      labelname = step
                  elif tool is not None:
                      labelname = f"{step}{index}\n({tool})"
                  else:
                      labelname = f"{step}{index}"
                  dot.node(node, label=labelname, bordercolor=fontcolor, style='filled',
                           fontcolor=fontcolor, fontsize=fontsize, ordering="in",
                           penwidth=penwidth, fillcolor=fillcolor)
                  # get inputs
                  all_inputs = []
                  for in_step, in_index in self.get('flowgraph', flow, step, index, 'input'):
                      all_inputs.append(in_step + in_index)
                  for item in all_inputs:
                      dot.edge(item, node)
          dot.render(filename=fileroot, cleanup=True)
  
      ########################################################################
      def _collect_paths(self):
          '''
          Returns list of paths to files that will be collected by import step.
  
          See docstring for _collect() for more details.
          '''
          paths = []
  
          copyall = self.get('option', 'copyall')
          allkeys = self.getkeys()
          for key in allkeys:
              leaftype = self.get(*key, field='type')
              if re.search('file', leaftype):
                  copy = self.get(*key, field='copy')
                  value = self.get(*key)
                  if copyall or copy:
                      for item in value:
                          paths.append(item)
  
          return paths
  
      ########################################################################
      def _collect(self, step, index):
          '''
          Collects files found in the configuration dictionary and places
          them in inputs/. The function only copies in files that have the 'copy'
          field set as true. If 'copyall' is set to true, then all files are
          copied in.
  
          1. indexing like in run, job1
          2. chdir package
          3. run tool to collect files, pickle file in output/design.v
          4. copy in rest of the files below
          5. record files read in to schema
  
          '''
  
          indir = 'inputs'
          flow = self.get('option', 'flow')
  
          if not os.path.exists(indir):
              os.makedirs(indir)
  
          self.logger.info('Collecting input sources')
  
          for path in self._collect_paths():
              filename = self._get_imported_filename(path)
              abspath = self._find_sc_file(path)
              if abspath:
                  self.logger.info(f"Copying {abspath} to '{indir}' directory")
                  shutil.copy(abspath, os.path.join(indir, filename))
              else:
                  self._haltstep(step, index)
  
          outdir = 'outputs'
          if not os.path.exists(outdir):
              os.makedirs(outdir)
  
          # Logic to make links from outputs/ to inputs/, skipping anything that
          # will be output by the tool as well as the manifest. We put this here
          # so that tools used for the import stage don't have to duplicate this
          # logic. We skip this logic for 'join'-based single-step imports, since
          # 'join' does the copy for us.
          tool = self.get('flowgraph', flow, step, index, 'tool')
          if tool not in self.builtin:
              if self.valid('tool', tool, 'output', step, index):
                  outputs = self.get('tool', tool, 'output', step, index)
              else:
                  outputs = []
              design = self.get('design')
              ignore = outputs + [f'{design}.pkg.json']
              utils.copytree(indir, outdir, dirs_exist_ok=True, link=True, ignore=ignore)
          elif tool not in ('join', 'nop'):
              self.error = 1
              self.logger.error(f'Invalid import step builtin {tool}. Must be tool or join.')
  
      ###########################################################################
      def archive(self, step=None, index=None, all_files=False):
          '''Archive a job directory.
  
          Creates a single compressed archive (.tgz) based on the design,
          jobname, and flowgraph in the current chip manifest. Individual
          steps and/or indices can be archived based on argumnets specified.
          By default, all steps and indices in the flowgraph are archived.
          By default, only the outputs directory content and the log file
          are archived.
  
          Args:
              step(str): Step to archive.
              index (str): Index to archive
              all_files (bool): If True, all files are archived.
  
          '''
  
          design = self.get('design')
          jobname = self.get('option', 'jobname')
          buildpath = self.get('option', 'builddir')
          flow = self.get('option', 'flow')
  
          if step:
              steplist = [step]
          elif self.get('arg', 'step'):
              steplist = [self.get('arg', 'step')]
          elif self.get('option', 'steplist'):
              steplist = self.get('option', 'steplist')
          else:
              steplist = self.list_steps()
  
          if step:
              archive_name = f"{design}_{jobname}_{step}.tgz"
          else:
              archive_name = f"{design}_{jobname}.tgz"
  
          with tarfile.open(archive_name, "w:gz") as tar:
              for step in steplist:
                  if index:
                      indexlist = [index]
                  else:
                      indexlist = self.getkeys('flowgraph', flow, step)
                  for item in indexlist:
                      basedir = os.path.join(buildpath, design, jobname, step, item)
                      if all_files:
                           tar.add(os.path.abspath(basedir), arcname=basedir)
                      else:
                          outdir = os.path.join(basedir,'outputs')
                          logfile = os.path.join(basedir, step+'.log')
                          tar.add(os.path.abspath(outdir), arcname=outdir)
                          if os.path.isfile(logfile):
                              tar.add(os.path.abspath(logfile), arcname=logfile)
  
          return archive_name
  
      ###########################################################################
      def hash_files(self, *keypath, algo='sha256', update=True):
          '''Generates hash values for a list of parameter files.
  
          Generates a a hash value for each file found in the keypath.
          If the  update variable is True, the has values are recorded in the
          'filehash' field of the parameter, following the order dictated by
          the files within the 'values' parameter field.
  
          Files are located using the find_files() function.
  
          The file hash calculation is performed basd on the 'algo' setting.
          Supported algorithms include SHA1, SHA224, SHA256, SHA384, SHA512,
          and MD5.
  
          Args:
              *keypath(str): Keypath to parameter.
              algo (str): Algorithm to use for file hash calculation
              update (bool): If True, the hash values are recorded in the
                  chip object manifest.
  
          Returns:
              A list of hash values.
  
          Examples:
              >>> hashlist = hash_files('sources')
               Hashlist gets list of hash values computed from 'sources' files.
          '''
  
          keypathstr = ','.join(keypath)
          #TODO: Insert into find_files?
          if 'file' not in self.get(*keypath, field='type'):
              self.logger.error(f"Illegal attempt to hash non-file parameter [{keypathstr}].")
              self.error = 1
          else:
              filelist = self.find_files(*keypath)
              #cycle through all paths
              hashlist = []
              if filelist:
                  self.logger.info(f'Computing hash value for [{keypathstr}]')
              for filename in filelist:
                  if os.path.isfile(filename):
                      #TODO: Implement algo selection
                      hashobj = hashlib.sha256()
                      with open(filename, "rb") as f:
                          for byte_block in iter(lambda: f.read(4096), b""):
                              hashobj.update(byte_block)
                      hash_value = hashobj.hexdigest()
                      hashlist.append(hash_value)
                  else:
                      self.error = 1
                      self.logger.info(f"Internal hashing error, file not found")
              # compare previous hash to new hash
              oldhash = self.get(*keypath,field='filehash')
              for i,item in enumerate(oldhash):
                  if item != hashlist[i]:
                      self.logger.error(f"Hash mismatch for [{keypath}]")
                      self.error = 1
              self.set(*keypath, hashlist, field='filehash', clobber=True)
  
  
      ###########################################################################
      def audit_manifest(self):
          '''Verifies the integrity of the post-run compilation manifest.
  
          Checks the integrity of the chip object implementation flow after
          the run() function has been completed. Errors, warnings, and debug
          messages are reported through the logger object.
  
          Audit checks performed include:
  
          * Time stamps
          * File modifications
          * Error and warning policy
          * IP and design origin
          * User access
          * License terms
          * Version checks
  
          Returns:
              Returns True if the manifest has integrity, else returns False.
  
          Example:
              >>> chip.audit_manifest()
              Audits the Chip object manifest and returns 0 if successful.
  
          '''
  
          return 0
  
  
      ###########################################################################
      def calc_area(self):
          '''Calculates the area of a rectilinear diearea.
  
          Uses the shoelace formulate to calculate the design area using
          the (x,y) point tuples from the 'diearea' parameter. If only diearea
          paramater only contains two points, then the first and second point
          must be the lower left and upper right points of the rectangle.
          (Ref: https://en.wikipedia.org/wiki/Shoelace_formula)
  
          Returns:
              Design area (float).
  
          Examples:
              >>> area = chip.calc_area()
  
          '''
  
          vertices = self.get('asic', 'diearea')
  
          if len(vertices) == 2:
              width = vertices[1][0] - vertices[0][0]
              height = vertices[1][1] - vertices[0][1]
              area = width * height
          else:
              area = 0.0
              for i in range(len(vertices)):
                  j = (i + 1) % len(vertices)
                  area += vertices[i][0] * vertices[j][1]
                  area -= vertices[j][0] * vertices[i][1]
              area = abs(area) / 2
  
          return area
  
      ###########################################################################
      def calc_yield(self, model='poisson'):
          '''Calculates raw die yield.
  
          Calculates the raw yield of the design as a function of design area
          and d0 defect density. Calculation can be done based on the poisson
          model (default) or the murphy model. The die area and the d0
          parameters are taken from the chip dictionary.
  
          * Poisson model: dy = exp(-area * d0/100).
          * Murphy model: dy = ((1-exp(-area * d0/100))/(area * d0/100))^2.
  
          Args:
              model (string): Model to use for calculation (poisson or murphy)
  
          Returns:
              Design yield percentage (float).
  
          Examples:
              >>> yield = chip.calc_yield()
              Yield variable gets yield value based on the chip manifest.
          '''
  
          d0 = self.get('pdk', 'd0')
          diearea = self.calc_area()
  
          if model == 'poisson':
              dy = math.exp(-diearea * d0/100)
          elif model == 'murphy':
              dy = ((1-math.exp(-diearea * d0/100))/(diearea * d0/100))**2
  
          return dy
  
      ##########################################################################
      def calc_dpw(self):
          '''Calculates dies per wafer.
  
          Calculates the gross dies per wafer based on the design area, wafersize,
          wafer edge margin, and scribe lines. The calculation is done by starting
          at the center of the wafer and placing as many complete design
          footprints as possible within a legal placement area.
  
          Returns:
              Number of gross dies per wafer (int).
  
          Examples:
              >>> dpw = chip.calc_dpw()
              Variable dpw gets gross dies per wafer value based on the chip manifest.
          '''
  
          #PDK information
          wafersize = self.get('pdk', 'wafersize')
          edgemargin = self.get('pdk', 'edgemargin')
          hscribe = self.get('pdk', 'hscribe')
          vscribe = self.get('pdk', 'vscribe')
  
          #Design parameters
          diesize = self.get('asic', 'diesize').split()
          diewidth = (diesize[2] - diesize[0])/1000
          dieheight = (diesize[3] - diesize[1])/1000
  
          #Derived parameters
          radius = wafersize/2 -edgemargin
          stepwidth = (diewidth + hscribe)
          stepheight = (dieheight + vscribe)
  
          #Raster dies out from center until you touch edge margin
          #Work quadrant by quadrant
          dies = 0
          for quad in ('q1', 'q2', 'q3', 'q4'):
              x = 0
              y = 0
              if quad == "q1":
                  xincr = stepwidth
                  yincr = stepheight
              elif quad == "q2":
                  xincr = -stepwidth
                  yincr = stepheight
              elif quad == "q3":
                  xincr = -stepwidth
                  yincr = -stepheight
              elif quad == "q4":
                  xincr = stepwidth
                  yincr = -stepheight
              #loop through all y values from center
              while math.hypot(0, y) < radius:
                  y = y + yincr
                  while math.hypot(x, y) < radius:
                      x = x + xincr
                      dies = dies + 1
                  x = 0
  
          return int(dies)
  
      ###########################################################################
      def grep(self, args, line):
          """
          Emulates the Unix grep command on a string.
  
          Emulates the behavior of the Unix grep command that is etched into
          our muscle memory. Partially implemented, not all features supported.
          The function returns None if no match is found.
  
          Args:
              arg (string): Command line arguments for grep command
              line (string): Line to process
  
          Returns:
              Result of grep command (string).
  
          """
  
          # Quick return if input is None
          if line is None:
              return None
  
          # Partial list of supported grep options
          options = {
              '-v' : False, # Invert the sense of matching
              '-i' : False, # Ignore case distinctions in patterns and data
              '-E' : False, # Interpret PATTERNS as extended regular expressions.
              '-e' : False, # Safe interpretation of pattern starting with "-"
              '-x' : False, # Select only matches that exactly match the whole line.
              '-o' : False, # Print only the match parts of a matching line
              '-w' : False} # Select only lines containing matches that form whole words.
  
          # Split into repeating switches and everything else
          match = re.match(r'\s*((?:\-\w\s)*)(.*)', args)
  
          pattern = match.group(2)
  
          # Split space separated switch string into list
          switches = match.group(1).strip().split(' ')
  
          # Find special -e switch update the pattern
          for i in range(len(switches)):
              if switches[i] == "-e":
                  if i != (len(switches)):
                      pattern = ' '.join(switches[i+1:]) + " " + pattern
                      switches = switches[0:i+1]
                      break
                  options["-e"] = True
              elif switches[i] in options.keys():
                  options[switches[i]] = True
              elif switches[i] !='':
                  print("ERROR",switches[i])
  
          #REGEX
          #TODO: add all the other optinos
          match = re.search(rf"({pattern})", line)
          if bool(match) == bool(options["-v"]):
              return None
          else:
              return line
  
      ###########################################################################
      def check_logfile(self, jobname=None, step=None, index='0',
                        logfile=None, display=True):
          '''
          Checks logfile for patterns found in the 'regex' parameter.
  
          Reads the content of the step's log file and compares the
          content found in step 'regex' parameter. The matches are
          stored in the file 'reports/<design>.<suffix>' in the run directory.
          The matches are printed to STDOUT if display is set to True.
  
          Args:
              step (str): Task step name ('syn', 'place', etc)
              jobname (str): Jobid directory name
              index (str): Task index
              display (bool): If True, printes matches to STDOUT.
  
          Examples:
              >>> chip.check_logfile('place')
              Searches for regex matches in the place logfile.
          '''
  
          # Using manifest to get defaults
  
          flow = self.get('option', 'flow')
  
          if jobname is None:
              jobname = self.get('option', 'jobname')
          if logfile is None:
              logfile = f"{step}.log"
          if step is None:
              step = self.get('arg', 'step')
          if index is None:
              index = self.getkeys('flowgraph', flow, step)[0]
  
          tool = self.get('flowgraph', flow, step, index, 'tool')
  
          # Creating local dictionary (for speed)
          # self.get is slow
          checks = {}
          regex_list = []
          if self.valid('tool', tool, 'regex', step, index, 'default'):
              regex_list = self.getkeys('tool', tool, 'regex', step, index)
          for suffix in regex_list:
              checks[suffix] = {}
              checks[suffix]['report'] = open(f"{step}.{suffix}", "w")
              checks[suffix]['args'] = self.get('tool', tool, 'regex', step, index, suffix)
  
          # Looping through patterns for each line
          with open(logfile) as f:
            for line in f:
                for suffix in checks:
                    string = line
                    for item in checks[suffix]['args']:
                        if string is None:
                            break
                        else:
                            string = self.grep(item, string)
                    if string is not None:
                        #always print to file
                        print(string.strip(), file=checks[suffix]['report'])
                        #selectively print to display
                        if display:
                            self.logger.info(string.strip())
  
      ###########################################################################
      def _find_leaves(self, steplist):
          '''Helper to find final (leaf) tasks for a given steplist.'''
          flow = self.get('option', 'flow')
  
          # First, iterate over the tasks to generate a set of non-leaf tasks.
          all_tasks = set()
          non_leaf_tasks = set()
          for step in steplist:
              for index in self.getkeys('flowgraph', flow, step):
                  all_tasks.add((step, index))
                  for in_step, in_index in self.get('flowgraph', flow, step, index, 'input'):
                      if in_step in steplist:
                          non_leaf_tasks.add((in_step, in_index))
  
          # Then, find all leaf tasks by elimination.
          return all_tasks.difference(non_leaf_tasks)
  
      ###########################################################################
      def summary(self, steplist=None, show_all_indices=False):
          '''
          Prints a summary of the compilation manifest.
  
          Metrics from the flowgraph steps, or steplist parameter if
          defined, are printed out on a per step basis. All metrics from the
          metric dictionary with weights set in the flowgraph dictionary are
          printed out.
  
          Args:
              show_all_indices (bool): If True, displays metrics for all indices
                  of each step. If False, displays metrics only for winning
                  indices.
  
          Examples:
              >>> chip.summary()
              Prints out a summary of the run to stdout.
          '''
  
          # display whole flowgraph if no steplist specified
          flow = self.get('option', 'flow')
          if not steplist:
              if self.get('option', 'steplist'):
                  steplist = self.get('option', 'steplist')
              else:
                  steplist = self.list_steps()
  
          # Find all tasks that are part of a "winning" path.
          selected_tasks = set()
          to_search = []
  
          # Start search with any successful leaf tasks.
          leaf_tasks = self._find_leaves(steplist)
          for task in leaf_tasks:
              if self.get('flowgraph', flow, *task, 'status') == TaskStatus.SUCCESS:
                  selected_tasks.add(task)
                  to_search.append(task)
  
          # Search backwards, saving anything that was selected by leaf tasks.
          while len(to_search) > 0:
              task = to_search.pop(-1)
              for selected in self.get('flowgraph', flow, *task, 'select'):
                  if selected not in selected_tasks:
                      selected_tasks.add(selected)
                      to_search.append(selected)
  
          # only report tool based steps functions
          for step in steplist.copy():
              if self.get('flowgraph',flow, step,'0','tool') in self.builtin:
                  index = steplist.index(step)
                  del steplist[index]
  
          # job directory
          jobdir = self._getworkdir()
  
          # Custom reporting modes
          paramlist = []
          for item in self.getkeys('option', 'param'):
              paramlist.append(item+"="+self.get('option', 'param', item))
  
          if paramlist:
              paramstr = ', '.join(paramlist)
          else:
              paramstr = "None"
  
          info_list = ["SUMMARY:\n",
                       "design : " + self.get('design'),
                       "params : " + paramstr,
                       "jobdir : "+ jobdir,
                       ]
  
          if self.get('option', 'mode') == 'asic':
              pdk = self.get('option', 'pdk')
              info_list.extend(["foundry : " + self.get('pdk', pdk, 'foundry'),
                                "process : " + pdk,
                                "targetlibs : "+" ".join(self.get('asic', 'logiclib'))])
          elif self.get('option', 'mode') == 'fpga':
              info_list.extend(["partname : "+self.get('fpga','partname')])
  
          info = '\n'.join(info_list)
  
  
          print("-"*135)
          print(info, "\n")
  
          # Stepping through all steps/indices and printing out metrics
          data = []
  
          #Creating Header
          header = []
          indices_to_show = {}
          colwidth = 8
          for step in steplist:
              if show_all_indices:
                  indices_to_show[step] = self.getkeys('flowgraph', flow, step)
              else:
                  indices_to_show[step] = []
                  for index in self.getkeys('flowgraph', flow, step):
                      if (step, index) in selected_tasks:
                          indices_to_show[step].append(index)
  
          # header for data frame
          for step in steplist:
              for index in indices_to_show[step]:
                  header.append(f'{step}{index}'.center(colwidth))
  
          # figure out which metrics have non-zero weights
          metric_list = []
          for step in steplist:
              for metric in self.getkeys('metric','default','default'):
                  if metric in self.getkeys('flowgraph', flow, step, '0', 'weight'):
                      if self.get('flowgraph', flow, step, '0', 'weight', metric) is not None:
                          if metric not in metric_list:
                              metric_list.append(metric)
  
          # print out all metrics
          metrics = []
          for metric in metric_list:
              metrics.append(" " + metric)
              row = []
              for step in steplist:
                  for index in indices_to_show[step]:
                      value = self.get('metric', step, index, metric)
  
                      if value is None:
                          value = 'ERR'
                      else:
                          value = str(value)
  
                      row.append(" " + value.center(colwidth))
              data.append(row)
  
          pandas.set_option('display.max_rows', 500)
          pandas.set_option('display.max_columns', 500)
          pandas.set_option('display.width', 100)
          df = pandas.DataFrame(data, metrics, header)
          print(df.to_string())
          print("-"*135)
  
          # Create a report for the Chip object which can be viewed in a web browser.
          # Place report files in the build's root directory.
          web_dir = os.path.join(self.get('option', 'builddir'),
                                 self.get('design'),
                                 self.get('option', 'jobname'))
          if os.path.isdir(web_dir):
              # Gather essential variables.
              templ_dir = os.path.join(self.scroot, 'templates', 'report')
              design = self.get('design')
              flow = self.get('option', 'flow')
              flow_steps = steplist
              flow_tasks = {}
              for step in flow_steps:
                  flow_tasks[step] = self.getkeys('flowgraph', flow, step)
  
              # Call 'show()' to generate a low-res PNG of the design.
              results_gds = self.find_result('gds', step='export')
              img_data = None
              if results_gds and not self.get('option', 'nodisplay'):
                  self.show(results_gds,
                              ['-rd', 'screenshot=1', '-rd', 'scr_w=1024', '-rd', 'scr_h=1024', '-z'])
                  result_file = os.path.join(web_dir, f'{design}.png')
                  # Result might not exist if there is no display
                  if os.path.isfile(result_file):
                      with open(result_file, 'rb') as img_file:
                          img_data = base64.b64encode(img_file.read()).decode('utf-8')
  
              # Generate results page by passing the Chip manifest into the Jinja2 template.
              env = Environment(loader=FileSystemLoader(templ_dir))
              results_page = os.path.join(web_dir, 'report.html')
              pruned_cfg = self._prune(self.cfg)
              if 'history' in pruned_cfg:
                  del pruned_cfg['history']
              if 'library' in pruned_cfg:
                  del pruned_cfg['library']
              with open(results_page, 'w') as wf:
                  wf.write(env.get_template('sc_report.j2').render(
                      manifest = self.cfg,
                      pruned_cfg = pruned_cfg,
                      metric_keys = metric_list,
                      metrics = self.cfg['metric'],
                      tasks = flow_tasks,
                      img_data = img_data,
                  ))
  
              # Try to open the results and layout only if '-nodisplay' is not set.
              if not self.get('option', 'nodisplay'):
                  try:
                      webbrowser.get(results_page)
                  except webbrowser.Error:
                      # Python 'webbrowser' module includes a limited number of popular defaults.
                      # Depending on the platform, the user may have defined their own with $BROWSER.
                      if 'BROWSER' in os.environ:
                          subprocess.run([os.environ['BROWSER'], results_page])
                      else:
                          self.logger.warning('Unable to open results page in web browser:\n' +
                                              os.path.abspath(os.path.join(web_dir, "report.html")))
  
      ###########################################################################
      def list_steps(self, flow=None):
          '''
          Returns an ordered list of flowgraph steps.
  
          All step keys from the flowgraph dictionary are collected and the
          distance from the root node (ie. without any inputs defined) is
          measured for each step. The step list is then sorted based on
          the distance from root and returned.
  
          Returns:
              A list of steps sorted by distance from the root node.
  
          Example:
              >>> steplist = chip.list_steps()
              Variable steplist gets list of steps sorted by distance from root.
          '''
  
          if flow is None:
              flow = self.get('option', 'flow')
  
          #Get length of paths from step to root
          depth = {}
          for step in self.getkeys('flowgraph', flow):
              depth[step] = 0
              for path in self._allpaths(self.cfg, flow, step, str(0)):
                  if len(list(path)) > depth[step]:
                      depth[step] = len(path)
  
          #Sort steps based on path lenghts
          sorted_dict = dict(sorted(depth.items(), key=lambda depth: depth[1]))
          return list(sorted_dict.keys())
  
      ###########################################################################
      def _allpaths(self, cfg, flow, step, index, path=None):
          '''Recursive helper for finding all paths from provided step, index to
          root node(s) with no inputs.
  
          Returns a list of lists.
          '''
  
          if path is None:
              path = []
  
          inputs = self.get('flowgraph', flow, step, index, 'input', cfg=cfg)
  
          if not self.get('flowgraph', flow, step, index, 'input', cfg=cfg):
              return [path]
          else:
              allpaths = []
              for in_step, in_index in inputs:
                  newpath = path.copy()
                  newpath.append(in_step + in_index)
                  allpaths.extend(self._allpaths(cfg, flow, in_step, in_index, path=newpath))
  
          return allpaths
  
      ###########################################################################
      def clock(self, pin, period, jitter=0):
          """
          Clock configuration helper function.
  
          A utility function for setting all parameters associated with a
          single clock definition in the schema.
  
          The method modifies the following schema parameters:
  
          ['datasheet', name, 'pin']
          ['datasheet', name, 'period']
          ['datasheet', name, 'jitter']
  
          Args:
              pin (str): Full hiearchical path to clk pin.
              period (float): Clock period specified in ns.
              jitter (float): Clock jitter specified in ns.
  
          Examples:
              >>> chip.clock('clk, period=1.0)
             Create a clock named 'clk' with a 1.0ns period.
          """
          design = self.get('design')
          self.set('datasheet', design, 'pin', pin, 'type', 'global', 'clk')
  
          period_range = (period * 1e-9, period * 1e-9, period * 1e-9)
          self.set('datasheet', design, 'pin', pin, 'tperiod', 'global', period_range)
  
          jitter_range = (jitter * 1e-9, jitter * 1e-9, jitter * 1e-9)
          self.set('datasheet', design, 'pin', pin, 'tjitter', 'global', jitter_range)
  
      ###########################################################################
      def node(self, flow, step, tool, index=0):
          '''
          Creates a flowgraph node.
  
          Creates a flowgraph node by binding a tool to a task. A task is defined
          as the combination of a step and index. A tool can be an external
          exeuctable or one of the built in functions in the SiliconCompiler
          framework). Built in functions include: minimum, maximum, join, mux,
          verify.
  
          The method modifies the following schema parameters:
  
          ['flowgraph', flow, step, index, 'tool', tool]
          ['flowgraph', flow, step, index, 'weight', metric]
  
          Args:
              flow (str): Flow name
              step (str): Task step name
              tool (str): Tool (or builtin function) to associate with task.
              index (int): Task index
  
          Examples:
              >>> chip.node('asicflow', 'place', 'openroad', index=0)
              Creates a task with step='place' and index=0 and binds it to the 'openroad' tool.
          '''
  
          # bind tool to node
          self.set('flowgraph', flow, step, str(index), 'tool', tool)
          # set default weights
          for metric in self.getkeys('metric', 'default', 'default'):
              self.set('flowgraph', flow, step, str(index), 'weight', metric, 0)
  
      ###########################################################################
      def edge(self, flow, tail, head, tail_index=0, head_index=0):
          '''
          Creates a directed edge from a tail node to a head node.
  
          Connects the output of a tail node with the input of a head node by
          setting the 'input' field of the head node in the schema flowgraph.
  
          The method modifies the following parameters:
  
          ['flowgraph', flow, head, str(head_index), 'input']
  
          Args:
              flow (str): Name of flow
              tail (str): Name of tail node
              head (str): Name of head node
              tail_index (int): Index of tail node to connect
              head_index (int): Index of head node to connect
  
          Examples:
              >>> chip.edge('place', 'cts')
              Creates a directed edge from place to cts.
          '''
  
          # Handling connecting edges between graphs
          # Not completely name space safe, but feels like this limitation
          # is a non-issue
  
          module_tail = f"{tail}.export"
          module_head = f"{head}.import"
          if module_tail in self.getkeys('flowgraph',flow):
              tail = module_tail
          if module_head in self.getkeys('flowgraph',flow):
              head = module_head
          #TODO: add error checking
          # Adding
          self.add('flowgraph', flow, head, str(head_index), 'input', (tail, str(tail_index)))
  
      ###########################################################################
      def graph(self, flow, subflow, name=None):
          '''
          Instantiates a named flow as a graph in the current flowgraph.
  
          Args:
              flow (str): Name of current flow.
              subflow (str): Name of flow to instantiate
              name (str): Name of instance
  
          Examples:
              >>> chip.graph('asicflow')
              Instantiates Creates a directed edge from place to cts.
          '''
  
          if flow not in self.getkeys('flowgraph'):
              self.cfg['flowgraph'][flow] ={}
  
          # uniquify each step
          for step in self.getkeys('flowgraph',subflow):
              if name is None:
                  newstep = step
              else:
                  newstep = name + "." + step
              if newstep not in self.getkeys('flowgraph', flow):
                  self.cfg['flowgraph'][flow][newstep] ={}
              # recursive copy
              for key in self._allkeys(self.cfg['flowgraph'][subflow][step]):
                  self._copyparam(self.cfg['flowgraph'][subflow][step],
                                  self.cfg['flowgraph'][flow][newstep],
                                  key)
              # update step names
              for index in self.getkeys('flowgraph', flow, newstep):
                  all_inputs = self.get('flowgraph', flow, newstep, index,'input')
                  self.set('flowgraph', flow, newstep, index,'input',[])
                  for in_step, in_index in all_inputs:
                      newin = name + "." + in_step
                      self.add('flowgraph', flow, newstep, index,'input',(newin,in_index))
  
  
      ###########################################################################
      def pipe(self, flow, plan):
          '''
          Creates a pipeline based on an order list of key values pairs.
          '''
  
          for item in plan:
              step = list(item.keys())[0]
              tool = list(item.values())[0]
              self.node(flow, step, tool)
              if step != 'import':
                  self.edge(flow, prevstep, step)
              prevstep = step
  
      ###########################################################################
      def join(self, *tasks):
          '''
          Merges outputs from a list of input tasks.
  
          Args:
              tasks(list): List of input tasks specified as (step,index) tuples.
  
          Returns:
              Input list
  
          Examples:
              >>> select = chip.join([('lvs','0'), ('drc','0')])
             Select gets the list [('lvs','0'), ('drc','0')]
          '''
  
          tasklist = list(tasks)
          sel_inputs = tasklist
  
          # no score for join, so just return 0
          return sel_inputs
  
      ###########################################################################
      def nop(self, *task):
          '''
          A no-operation that passes inputs to outputs.
  
          Args:
              task(list): Input task specified as a (step,index) tuple.
  
          Returns:
              Input task
  
          Examples:
              >>> select = chip.nop(('lvs','0'))
             Select gets the tuple [('lvs',0')]
          '''
  
          return list(task)
  
      ###########################################################################
      def minimum(self, *tasks):
          '''
          Selects the task with the minimum metric score from a list of inputs.
  
          Sequence of operation:
  
          1. Check list of input tasks to see if all metrics meets goals
          2. Check list of input tasks to find global min/max for each metric
          3. Select MIN value if all metrics are met.
          4. Normalize the min value as sel = (val - MIN) / (MAX - MIN)
          5. Return normalized value and task name
  
          Meeting metric goals takes precedence over compute metric scores.
          Only goals with values set and metrics with weights set are considered
          in the calculation.
  
          Args:
              tasks(list): List of input tasks specified as (step,index) tuples.
  
          Returns:
              tuple containing
  
              - score (float): Minimum score
              - task (tuple): Task with minimum score
  
          Examples:
              >>> (score, task) = chip.minimum([('place','0'),('place','1')])
  
          '''
          return self._minmax(*tasks, op="minimum")
  
      ###########################################################################
      def maximum(self, *tasks):
          '''
          Selects the task with the maximum metric score from a list of inputs.
  
          Sequence of operation:
  
          1. Check list of input tasks to see if all metrics meets goals
          2. Check list of input tasks to find global min/max for each metric
          3. Select MAX value if all metrics are met.
          4. Normalize the min value as sel = (val - MIN) / (MAX - MIN)
          5. Return normalized value and task name
  
          Meeting metric goals takes precedence over compute metric scores.
          Only goals with values set and metrics with weights set are considered
          in the calculation.
  
          Args:
              tasks(list): List of input tasks specified as (step,index) tuples.
  
          Returns:
              tuple containing
  
              - score (float): Maximum score.
              - task (tuple): Task with minimum score
  
          Examples:
              >>> (score, task) = chip.maximum([('place','0'),('place','1')])
  
          '''
          return self._minmax(*tasks, op="maximum")
  
      ###########################################################################
      def _minmax(self, *steps, op="minimum", **selector):
          '''
          Shared function used for min and max calculation.
          '''
  
          if op not in ('minimum', 'maximum'):
              raise ValueError('Invalid op')
  
          flow = self.get('option', 'flow')
          steplist = list(steps)
  
          # Keeping track of the steps/indexes that have goals met
          failed = {}
          for step, index in steplist:
              if step not in failed:
                  failed[step] = {}
              failed[step][index] = False
  
              if self.get('flowgraph', flow, step, index, 'status') == TaskStatus.ERROR:
                  failed[step][index] = True
              else:
                  for metric in self.getkeys('metric', step, index):
                      if self.valid('flowgraph', flow, step, index, 'goal', metric):
                          goal = self.get('flowgraph', flow, step, index, 'goal', metric)
                          real = self.get('metric', step, index, metric)
                          if abs(real) > goal:
                              self.logger.warning(f"Step {step}{index} failed "
                                  f"because it didn't meet goals for '{metric}' "
                                  "metric.")
                              failed[step][index] = True
  
          # Calculate max/min values for each metric
          max_val = {}
          min_val = {}
          for metric in self.getkeys('flowgraph', flow, step, '0', 'weight'):
              max_val[metric] = 0
              min_val[metric] = float("inf")
              for step, index in steplist:
                  if not failed[step][index]:
                      real = self.get('metric', step, index, metric)
                      max_val[metric] = max(max_val[metric], real)
                      min_val[metric] = min(min_val[metric], real)
  
          # Select the minimum index
          best_score = float('inf') if op == 'minimum' else float('-inf')
          winner = None
          for step, index in steplist:
              if failed[step][index]:
                  continue
  
              score = 0.0
              for metric in self.getkeys('flowgraph', flow, step, index, 'weight'):
                  weight = self.get('flowgraph', flow, step, index, 'weight', metric)
                  if not weight:
                      # skip if weight is 0 or None
                      continue
  
                  real = self.get('metric', step, index, metric)
  
                  if not (max_val[metric] - min_val[metric]) == 0:
                      scaled = (real - min_val[metric]) / (max_val[metric] - min_val[metric])
                  else:
                      scaled = max_val[metric]
                  score = score + scaled * weight
  
              if ((op == 'minimum' and score < best_score) or
                  (op == 'maximum' and score > best_score)):
                  best_score = score
                  winner = (step,index)
  
          return (best_score, winner)
  
      ###########################################################################
      def verify(self, *tasks, **assertion):
          '''
          Tests an assertion on a list of input tasks.
  
          The provided steplist is verified to ensure that all assertions
          are True. If any of the assertions fail, False is returned.
          Assertions are passed in as kwargs, with the key being a metric
          and the value being a number and an optional conditional operator.
          The allowed conditional operators are: >, <, >=, <=
  
          Args:
              *steps (str): List of steps to verify
              **assertion (str='str'): Assertion to check on metric
  
          Returns:
              True if all assertions hold True for all steps.
  
          Example:
              >>> pass = chip.verify(['drc','lvs'], errors=0)
              Pass is True if the error metrics in the drc, lvs steps is 0.
          '''
          #TODO: implement
          return True
  
      ###########################################################################
      def mux(self, *tasks, **selector):
          '''
          Selects a task from a list of inputs.
  
          The selector criteria provided is used to create a custom function
          for selecting the best step/index pair from the inputs. Metrics and
          weights are passed in and used to select the step/index based on
          the minimum or maximum score depending on the 'op' argument.
  
          The function can be used to bypass the flows weight functions for
          the purpose of conditional flow execution and verification.
  
          Args:
              *steps (str): List of steps to verify
              **selector: Key value selection criteria.
  
          Returns:
              True if all assertions hold True for all steps.
  
          Example:
              >>> sel_stepindex = chip.mux(['route'], wirelength=0)
              Selects the routing stepindex with the shortest wirelength.
          '''
  
          #TODO: modify the _minmax function to feed in alternate weight path
          return None
  
      ###########################################################################
      def _runtask(self, step, index, status):
          '''
          Private per step run method called by run().
  
          The method takes in a step string and index string to indicated what
          to run.
  
          Execution flow:
          T1. Start wall timer
          T2. Defer job to compute node if using job scheduler
          T3. Set up working directory + chdir
          T4. Merge manifests from all input dependancies
          T5. Write manifest to input directory for convenience
          T6. Reset all metrics to 0 (consider removing)
          T7. Select inputs
          T8. Copy data from previous step outputs into inputs
          T9. Check manifest
          T10. Run pre_process() function
          T11. Set environment variables
          T12. Check EXE version
          T13. Save manifest as TCL/YAML
          T14. Start CPU timer
          T15. Run EXE
          T16. stop CPU timer
          T17. Run post_process()
          T18. Check log file
          T19. Hash all task files
          T20. Stop Wall timer
          T21. Make a task record
          T22. Save manifest to disk
          T23. Halt if any errors found
          T24. Clean up
          T25. chdir
  
          Note that since _runtask occurs in its own process with a separate
          address space, any changes made to the `self` object will not
          be reflected in the parent. We rely on reading/writing the chip manifest
          to the filesystem to communicate updates between processes.
          '''
  
          self._init_logger(step, index, in_run=True)
  
          ##################
          # Shared parameters (long function!)
          design = self.get('design')
          flow = self.get('option', 'flow')
          tool = self.get('flowgraph', flow, step, index, 'tool')
          quiet = self.get('option', 'quiet') and (step not in self.get('option', 'bkpt'))
  
          ##################
          # 1. Start wall timer
          wall_start = time.time()
  
          ##################
          # 2. Defer job to compute node
          # If the job is configured to run on a cluster, collect the schema
          # and send it to a compute node for deferred execution.
          # (Run the initial 'import' stage[s] locally)
  
          if self.get('option', 'jobscheduler') and \
             self.get('flowgraph', flow, step, index, 'input'):
              # Note: The _deferstep method blocks until the compute node
              # finishes processing this step, and it sets the active/error bits.
              _deferstep(self, step, index, status)
              return
  
          ##################
          # 3. Directory setup
          # support for sharing data across jobs
          job = self.get('option', 'jobname')
          in_job = job
          if step in self.getkeys('option', 'jobinput'):
              if index in self.getkeys('option', 'jobinput', step):
                  in_job = self.get('option', 'jobinput', step, index)
  
          workdir = self._getworkdir(step=step,index=index)
          cwd = os.getcwd()
          if os.path.isdir(workdir):
              shutil.rmtree(workdir)
          os.makedirs(workdir, exist_ok=True)
  
          os.chdir(workdir)
          os.makedirs('outputs', exist_ok=True)
          os.makedirs('reports', exist_ok=True)
  
          ##################
          # 4. Merge manifests from all input dependancies
  
          all_inputs = []
          if not self.get('option', 'remote'):
              for in_step, in_index in self.get('flowgraph', flow, step, index, 'input'):
                  in_task_status = status[in_step + in_index]
                  self.set('flowgraph', flow, in_step, in_index, 'status', in_task_status)
                  if in_task_status != TaskStatus.ERROR:
                      cfgfile = f"../../../{in_job}/{in_step}/{in_index}/outputs/{design}.pkg.json"
                      self._read_manifest(cfgfile, clobber=False, partial=True)
  
          ##################
          # 5. Write manifest prior to step running into inputs
  
          self.set('arg', 'step', None, clobber=True)
          self.set('arg', 'index', None, clobber=True)
          os.makedirs('inputs', exist_ok=True)
          #self.write_manifest(f'inputs/{design}.pkg.json')
  
          ##################
          # 6. Make metrics zero
          # TODO: There should be no need for this, but need to fix
          # without it we need to be more careful with flows to make sure
          # things like the builtin functions don't look at None values
          for metric in self.getkeys('metric', 'default', 'default'):
              self.set('metric', step, index, metric, 0)
  
          ##################
          # 7. Select inputs
  
          args = self.get('flowgraph', flow, step, index, 'args')
          inputs = self.get('flowgraph', flow, step, index, 'input')
  
          sel_inputs = []
          score = 0
  
          if tool in self.builtin:
              self.logger.info(f"Running built in task '{tool}'")
              # Figure out which inputs to select
              if tool == 'minimum':
                  (score, sel_inputs) = self.minimum(*inputs)
              elif tool == "maximum":
                  (score, sel_inputs) = self.maximum(*inputs)
              elif tool == "mux":
                  (score, sel_inputs) = self.mux(*inputs, selector=args)
              elif tool == "join":
                  sel_inputs = self.join(*inputs)
              elif tool == "verify":
                  if not self.verify(*inputs, assertion=args):
                      self._haltstep(step, index)
          else:
              sel_inputs = self.get('flowgraph', flow, step, index, 'input')
  
          if sel_inputs == None:
              self.logger.error(f'No inputs selected after running {tool}')
              self._haltstep(step, index)
  
          self.set('flowgraph', flow, step, index, 'select', sel_inputs)
  
          ##################
          # 8. Copy (link) output data from previous steps
  
          if step == 'import':
              self._collect(step, index)
  
          if not self.get('flowgraph', flow, step, index,'input'):
              all_inputs = []
          elif not self.get('flowgraph', flow, step, index, 'select'):
              all_inputs = self.get('flowgraph', flow, step, index,'input')
          else:
              all_inputs = self.get('flowgraph', flow, step, index, 'select')
          for in_step, in_index in all_inputs:
              if self.get('flowgraph', flow, in_step, in_index, 'status') == TaskStatus.ERROR:
                  self.logger.error(f'Halting step due to previous error in {in_step}{in_index}')
                  self._haltstep(step, index)
  
              # Skip copying pkg.json files here, since we write the current chip
              # configuration into inputs/{design}.pkg.json earlier in _runstep.
              utils.copytree(f"../../../{in_job}/{in_step}/{in_index}/outputs", 'inputs/', dirs_exist_ok=True,
                  ignore=[f'{design}.pkg.json'], link=True)
  
          ##################
          # 9. Check manifest
          self.set('arg', 'step', step, clobber=True)
          self.set('arg', 'index', index, clobber=True)
  
          if not self.get('option', 'skipcheck'):
              if self.check_manifest():
                  self.logger.error(f"Fatal error in check_manifest()! See previous errors.")
                  self._haltstep(step, index)
  
          ##################
          # 10. Run preprocess step for tool
          if tool not in self.builtin:
              func = self.find_function(tool, "pre_process", 'tools')
              if func:
                  func(self)
                  if self.error:
                      self.logger.error(f"Pre-processing failed for '{tool}'")
                      self._haltstep(step, index)
  
          ##################
          # 11. Set environment variables
  
          # License file configuration.
          for item in self.getkeys('tool', tool, 'licenseserver'):
              license_file = self.get('tool', tool, 'licenseserver', item)
              if license_file:
                  os.environ[item] = ':'.join(license_file)
  
          # Tool-specific environment variables for this task.
          if (step in self.getkeys('tool', tool, 'env')) and \
             (index in self.getkeys('tool', tool, 'env', step)):
              for item in self.getkeys('tool', tool, 'env', step, index):
                  os.environ[item] = self.get('tool', tool, 'env', step, index, item)
  
          ##################
          # 12. Check exe version
  
          vercheck = not self.get('option', 'novercheck')
          veropt = self.get('tool', tool, 'vswitch')
          exe = self._getexe(tool)
          version = None
          toolpath = exe # For record
          if exe is not None:
              exe_path, exe_base = os.path.split(exe)
              if veropt:
                  cmdlist = [exe]
                  cmdlist.extend(veropt)
                  proc = subprocess.run(cmdlist, stdout=PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
                  parse_version = self.find_function(tool, 'parse_version', 'tools')
                  if parse_version is None:
                      self.logger.error(f'{tool} does not implement parse_version.')
                      self._haltstep(step, index)
                  version = parse_version(proc.stdout)
  
                  self.logger.info(f"Tool '{exe_base}' found with version '{version}' in directory '{exe_path}'")
                  if vercheck and not self._check_version(version, tool):
                      self._haltstep(step, index)
              else:
                  self.logger.info(f"Tool '{exe_base}' found in directory '{exe_path}'")
          elif tool not in self.builtin:
              exe_base = self.get('tool', tool, 'exe')
              self.logger.error(f'Executable {exe_base} not found')
              self._haltstep(step, index)
  
          ##################
          # 13. Write manifest (tool interface) (Don't move this!)
          suffix = self.get('tool', tool, 'format')
          if suffix:
              pruneopt = bool(suffix!='tcl')
              self.write_manifest(f"sc_manifest.{suffix}", prune=pruneopt, abspath=True)
  
          ##################
          # 14. Start CPU Timer
          self.logger.debug(f"Starting executable")
          cpu_start = time.time()
  
          ##################
          # 15. Run executable (or copy inputs to outputs for builtin functions)
  
          # TODO: Currently no memory usage tracking in breakpoints, builtins, or unexpected errors.
          max_mem_bytes = 0
  
          if tool in self.builtin:
              utils.copytree(f"inputs", 'outputs', dirs_exist_ok=True, link=True)
          elif not self.get('option', 'skipall'):
              cmdlist = self._makecmd(tool, step, index)
              exe_base = os.path.basename(cmdlist[0])
              cmdstr = ' '.join([exe_base] + cmdlist[1:])
              self.logger.info('Running in %s', workdir)
              self.logger.info('%s', cmdstr)
              timeout = self.get('flowgraph', flow, step, index, 'timeout')
              logfile = step + '.log'
              if sys.platform in ('darwin', 'linux') and step in self.get('option', 'bkpt'):
                  # When we break on a step, the tool often drops into a shell.
                  # However, our usual subprocess scheme seems to break terminal
                  # echo for some tools. On POSIX-compatible systems, we can use
                  # pty to connect the tool to our terminal instead. This code
                  # doesn't handle quiet/timeout logic, since we don't want either
                  # of these features for an interactive session. Logic for
                  # forwarding to file based on
                  # https://docs.python.org/3/library/pty.html#example.
                  logfile = step + '.log'
                  with open(logfile, 'wb') as log_writer:
                      def read(fd):
                          data = os.read(fd, 1024)
                          log_writer.write(data)
                          return data
                      import pty # Note: this import throws exception on Windows
                      retcode = pty.spawn(cmdlist, read)
              else:
                  stdout_file = ''
                  stdout_suffix = self.get('tool', tool, 'stdout', step, index, 'suffix')
                  if self.get('tool', tool, 'stdout', step, index, 'destination') == 'log':
                      stdout_file = step + "." + stdout_suffix
                  elif self.get('tool', tool, 'stdout', step, index, 'destination') == 'output':
                      stdout_file =  os.path.join('outputs', self.get('design')) + "." + stdout_suffix
                  elif self.get('tool', tool, 'stdout', step, index, 'destination') == 'none':
                      stdout_file =  os.devnull
                  else:
                      destination = self.get('tool', tool, 'stdout', step, index, 'destination')
                      self.logger.error(f'stdout/destination has no support for {destination}. Use [log|output|none].')
                      self._haltstep(step, index)
                  stderr_file = ''
                  stderr_suffix = self.get('tool', tool, 'stderr', step, index, 'suffix')
                  if self.get('tool', tool, 'stderr', step, index, 'destination') == 'log':
                      stderr_file = step + "." + stderr_suffix
                  elif self.get('tool', tool, 'stderr', step, index, 'destination') == 'output':
                      stderr_file =  os.path.join('outputs', self.get('design')) + "." + stderr_suffix
                  elif self.get('tool', tool, 'stderr', step, index, 'destination') == 'none':
                      stderr_file =  os.devnull
                  else:
                      destination = self.get('tool', tool, 'stderr', step, index, 'destination')
                      self.logger.error(f'stderr/destination has no support for {destination}. Use [log|output|none].')
                      self._haltstep(step, index)
  
                  with open(stdout_file, 'w') as stdout_writer, open(stdout_file, 'r') as stdout_reader, open(stderr_file, 'w') as stderr_writer, open(stderr_file, 'r') as stderr_reader:
                      # Use separate reader/writer file objects as hack to display
                      # live output in non-blocking way, so we can monitor the
                      # timeout. Based on https://stackoverflow.com/a/18422264.
                      is_stdout_log = self.get('tool', tool, 'stdout', step, index, 'destination') == 'log'
                      is_stderr_log = self.get('tool', tool, 'stderr', step, index, 'destination') == 'log' and stderr_file != stdout_file
                      # if STDOUT and STDERR are to be redirected to the same file,
                      # use a single writer
                      if stderr_file == stdout_file:
                          stderr_writer.close()
                          stderr_reader.close()
                          stderr_writer = subprocess.STDOUT
  
                      cmd_start_time = time.time()
                      proc = subprocess.Popen(cmdlist,
                                              stdout=stdout_writer,
                                              stderr=stderr_writer)
  
                      while proc.poll() is None:
                          # Gather subprocess memory usage.
                          try:
                              pproc = psutil.Process(proc.pid)
                              max_mem_bytes = max(max_mem_bytes, pproc.memory_full_info().uss)
                          except psutil.Error:
                              # Process may have already terminated or been killed.
                              # Retain existing memory usage statistics in this case.
                              pass
  
                          # Loop until process terminates
                          if not quiet:
                              if is_stdout_log:
                                  sys.stdout.write(stdout_reader.read())
                              if is_stderr_log:
                                  sys.stdout.write(stderr_reader.read())
                          if timeout is not None and time.time() - cmd_start_time > timeout:
                              self.logger.error(f'Step timed out after {timeout} seconds')
                              proc.terminate()
                              self._haltstep(step, index)
                          time.sleep(0.1)
  
                      # Read the remaining
                      if not quiet:
                          if is_stdout_log:
                              sys.stdout.write(stdout_reader.read())
                          if is_stderr_log:
                              sys.stdout.write(stderr_reader.read())
                      retcode = proc.returncode
  
              if retcode != 0:
                  self.logger.warning('Command failed with code %d. See log file %s', retcode, os.path.abspath(logfile))
                  self._haltstep(step, index)
  
          ##################
          # 16. Capture cpu runtime and memory footprint.
          cpu_end = time.time()
          cputime = round((cpu_end - cpu_start),2)
          self.set('metric', step, index, 'exetime', cputime)
          self.set('metric', step, index, 'memory', max_mem_bytes)
  
          ##################
          # 17. Post process
          if (tool not in self.builtin) and (not self.get('option', 'skipall')) :
              func = self.find_function(tool, 'post_process', 'tools')
              if func:
                  func(self)
  
          ##################
          # 18. Check log file (must be after post-process)
          if (tool not in self.builtin) and (not self.get('option', 'skipall')) :
              self.check_logfile(step=step, index=index, display=not quiet)
  
          ##################
          # 19. Hash files
          if self.get('option', 'hash') and (tool not in self.builtin):
              # hash all outputs
              self.hash_files('tool', tool, 'output', step, index)
              # hash all requirements
              if self.valid('tool', tool, 'require', step, index, quiet=True):
                  for item in self.get('tool', tool, 'require', step, index):
                      args = item.split(',')
                      if 'file' in self.get(*args, field='type'):
                          self.hash_files(*args)
  
          ##################
          # 20. Capture wall runtime and cpu cores
          wall_end = time.time()
          walltime = round((wall_end - wall_start),2)
          self.set('metric',step, index, 'tasktime', walltime)
          self.logger.info(f"Finished task in {walltime}s")
  
          ##################
          # 21. Make a record if tracking is enabled
          if self.get('option', 'track'):
              self._make_record(step, index, wall_start, wall_end, version, toolpath, cmdlist[1:])
  
          ##################
          # 22. Save a successful manifest
          self.set('flowgraph', flow, step, index, 'status', TaskStatus.SUCCESS)
          self.set('arg', 'step', None, clobber=True)
          self.set('arg', 'index', None, clobber=True)
  
          self.write_manifest(os.path.join("outputs", f"{design}.pkg.json"))
  
          ##################
          # 23. Stop if there are errors
          errors = self.get('metric', step, index, 'errors')
          if errors > 0 and not self.get('option', 'flowcontinue'):
              self.logger.error(f'{tool} reported {errors} errors during {step}{index}')
              self._haltstep(step, index)
  
          ##################
          # 24. Clean up non-essential files
          if self.get('option', 'clean'):
              self._eda_clean(tool, step, index)
  
          ##################
          # 25. return to original directory
          os.chdir(cwd)
  
      ###########################################################################
      def _haltstep(self, step, index, log=True):
          if log:
              self.logger.error(f"Halting step '{step}' index '{index}' due to errors.")
          sys.exit(1)
  
      ###########################################################################
      def _eda_clean(self, tool, step, index):
          '''Cleans up work directory of unecessary files.
  
          Assumes our cwd is the workdir for step and index.
          '''
  
          keep = ['inputs', 'outputs', 'reports', f'{step}.log', 'replay.sh']
  
          manifest_format = self.get('tool', tool, 'format')
          if manifest_format:
              keep.append(f'sc_manifest.{manifest_format}')
  
          for suffix in self.getkeys('tool', tool, 'regex', step, index):
              keep.append(f'{step}.{suffix}')
  
          # Tool-specific keep files
          if self.valid('tool', tool, 'keep', step, index):
              keep.extend(self.get('tool', tool, 'keep', step, index))
  
          for path in os.listdir():
              if path in keep:
                  continue
              if os.path.isdir(path):
                  shutil.rmtree(path)
              else:
                  os.remove(path)
  
      ###########################################################################
      def run(self):
          '''
          Executes tasks in a flowgraph.
  
          The run function sets up tools and launches runs for every index
          in a step defined by a steplist. The steplist is taken from the schema
          steplist parameter if defined, otherwise the steplist is defined
          as the list of steps within the schema flowgraph dictionary. Before
          starting  the process, tool modules are loaded and setup up for each
          step and index based on on the schema eda dictionary settings.
          Once the tools have been set up, the manifest is checked using the
          check_manifest() function and files in the manifest are hashed based
          on the 'hashmode' schema setting.
  
          Once launched, each process waits for preceding steps to complete,
          as defined by the flowgraph 'inputs' parameter. Once a all inputs
          are ready, previous steps are checked for errors before the
          process entered a local working directory and starts to run
          a tool or to execute a built in Chip function.
  
          Fatal errors within a step/index process cause all subsequent
          processes to exit before start, returning control to the the main
          program which can then exit.
  
          Examples:
              >>> run()
              Runs the execution flow defined by the flowgraph dictionary.
          '''
  
          flow = self.get('option', 'flow')
  
          # Re-init logger to include run info after setting up flowgraph.
          self._init_logger(in_run=True)
  
          # Run steps if set, otherwise run whole graph
          if self.get('arg', 'step'):
              steplist = [self.get('arg', 'step')]
          elif self.get('option', 'steplist'):
              steplist = self.get('option', 'steplist')
          else:
              steplist = self.list_steps()
  
              if not self.get('option', 'resume'):
                  # If no step(list) was specified, the whole flow is being run
                  # start-to-finish. Delete the build dir to clear stale results.
                  cur_job_dir = self._getworkdir()
                  if os.path.isdir(cur_job_dir):
                      shutil.rmtree(cur_job_dir)
  
          # List of indices to run per step. Precomputing this ensures we won't
          # have any problems if [arg, index] gets clobbered, and reduces logic
          # repetition.
          indexlist = {}
          for step in steplist:
              if self.get('arg', 'index'):
                  indexlist[step] = [self.get('arg', 'index')]
              elif self.get('option', 'indexlist'):
                  indexlist[step] = self.get("option", 'indexlist')
              else:
                  indexlist[step] = self.getkeys('flowgraph', flow, step)
  
          # Reset flowgraph/records/metrics by probing build directory. We need
          # to set values to None for steps we may re-run so that merging
          # manifests from _runtask() actually updates values.
          should_resume = self.get("option", 'resume')
          for step in self.getkeys('flowgraph', flow):
              all_indices_failed = True
              for index in self.getkeys('flowgraph', flow, step):
                  stepdir = self._getworkdir(step=step, index=index)
                  cfg = f"{stepdir}/outputs/{self.get('design')}.pkg.json"
  
                  in_steplist = step in steplist and index in indexlist[step]
                  if not os.path.isdir(stepdir) or (in_steplist and not should_resume):
                      # If stepdir doesn't exist, we need to re-run this task. If
                      # we're not running with -resume, we also re-run anything
                      # in the steplist.
                      self.set('flowgraph', flow, step, index, 'status', None)
                      for metric in self.getkeys('metric', 'default', 'default'):
                          self.set('metric', step, index, metric, None)
                      for record in self.getkeys('record', 'default', 'default'):
                          self.set('record', step, index, record, None)
                  elif os.path.isfile(cfg):
                      self.set('flowgraph', flow, step, index, 'status', TaskStatus.SUCCESS)
                      all_indices_failed = False
                  else:
                      self.set('flowgraph', flow, step, index, 'status', TaskStatus.ERROR)
  
              if should_resume and all_indices_failed and step in steplist:
                  # When running with -resume, we re-run any step in steplist that
                  # had all indices fail.
                  for index in self.getkeys('flowgraph', flow, step):
                      if index in indexlist[step]:
                          self.set('flowgraph', flow, step, index, 'status', None)
                          for metric in self.getkeys('metric', 'default', 'default'):
                              self.set('metric', step, index, metric,  None)
                          for record in self.getkeys('record', 'default', 'default'):
                              self.set('record', step, index, record, None)
  
          # Set env variables
          for envvar in self.getkeys('option', 'env'):
              val = self.get('option', 'env', envvar)
              os.environ[envvar] = val
  
          # Remote workflow: Dispatch the Chip to a remote server for processing.
          if self.get('option','remote'):
              # Load the remote storage config into the status dictionary.
              if self.get('option','credentials'):
                  # Use the provided remote credentials file.
                  cfg_file = self.get('option','credentials')[-1]
                  cfg_dir = os.path.dirname(cfg_file)
              else:
                  # Use the default config file path.
                  cfg_dir = os.path.join(Path.home(), '.sc')
                  cfg_file = os.path.join(cfg_dir, 'credentials')
              if (not os.path.isdir(cfg_dir)) or (not os.path.isfile(cfg_file)):
                  self.logger.error('Could not find remote server configuration - please run "sc-configure" and enter your server address and credentials.')
                  raise SiliconCompilerError('Valid remote credentials could not be found.')
              with open(cfg_file, 'r') as cfgf:
                  self.status['remote_cfg'] = json.loads(cfgf.read())
              if (not 'address' in self.status['remote_cfg']):
                  self.logger.error('Improperly formatted remote server configuration - please run "sc-configure" and enter your server address and credentials.')
                  raise SiliconCompilerError('Valid remote credentials could not be found.')
  
              # Pre-process: Run an 'import' stage locally, and upload the
              # in-progress build directory to the remote server.
              # Data is encrypted if user / key were specified.
              # run remote process
              remote_preprocess(self)
  
              # Run the job on the remote server, and wait for it to finish.
              remote_run(self)
  
              # Fetch results (and delete the job's data from the server).
              fetch_results(self)
  
              # Read back configuration from final manifest.
              cfg = os.path.join(self._getworkdir(),f"{self.get('design')}.pkg.json")
              if os.path.isfile(cfg):
                  local_dir = self.get('option','builddir')
                  self.read_manifest(cfg, clobber=True, clear=True)
                  self.set('option', 'builddir', local_dir)
              else:
                  # Hack to find first failed step by checking for presence of
                  # output manifests.
                  # TODO: fetch_results() should return info about step failures.
                  failed_step = steplist[-1]
                  for step in steplist[:-1]:
                      step_has_cfg = False
                      for index in indexlist[step]:
                          stepdir = self._getworkdir(step=step, index=index)
                          cfg = f"{stepdir}/outputs/{self.get('design')}.pkg.json"
                          if os.path.isfile(cfg):
                              step_has_cfg = True
                              break
  
                      if not step_has_cfg:
                          failed_step = step
                          break
  
                  stepdir = self._getworkdir(step=failed_step)[:-1]
                  raise SiliconCompilerError(f'Run() failed on step {failed_step}! '
                      f'See logs in {stepdir} for error details.')
          else:
              status = {}
  
              # Populate status dict with any flowgraph status values that have already
              # been set.
              for step in self.getkeys('flowgraph', flow):
                  for index in self.getkeys('flowgraph', flow, step):
                      stepstr = step + index
                      task_status = self.get('flowgraph', flow, step, index, 'status')
                      if task_status is not None:
                          status[step + index] = task_status
                      else:
                          status[step + index] = TaskStatus.PENDING
  
              # Setup tools for all tasks to run.
              for step in steplist:
                  for index in indexlist[step]:
                      # Setting up tool is optional
                      tool = self.get('flowgraph', flow, step, index, 'tool')
                      if tool not in self.builtin:
                          self.set('arg','step', step)
                          self.set('arg','index', index)
                          func = self.find_function(tool, 'setup', 'tools')
                          if func is None:
                              self.logger.error(f'setup() not found for tool {tool}')
                              sys.exit(1)
                          func(self)
                          # Need to clear index, otherwise we will skip
                          # setting up other indices. Clear step for good
                          # measure.
                          self.set('arg','step', None)
                          self.set('arg','index', None)
  
              # Implement auto-update of jobincrement
              try:
                  alljobs = os.listdir(self.get('option','builddir') + "/" + self.get('design'))
                  if self.get('option','jobincr'):
                      jobid = 0
                      for item in alljobs:
                          m = re.match(self.get('option','jobname')+r'(\d+)', item)
                          if m:
                              jobid = max(jobid, int(m.group(1)))
                      self.set('option', 'jobid', str(jobid + 1))
              except:
                  pass
  
              # Check validity of setup
              self.logger.info("Checking manifest before running.")
              if not self.get('option','skipcheck'):
                  self.check_manifest()
  
              # Check if there were errors before proceeding with run
              if self.error:
                  self.logger.error(f"Check failed. See previous errors.")
                  raise SiliconCompilerError(f"Manifest checks failed.")
  
              # For each task to run, prepare a process and store its dependencies
              jobname = self.get('option','jobname')
              tasks_to_run = {}
              processes = {}
              for step in steplist:
                  for index in indexlist[step]:
                      if status[step+index] != TaskStatus.PENDING:
                          continue
  
                      inputs = [step+index for step, index in self.get('flowgraph', flow, step, index, 'input')]
  
                      if (step in self.getkeys('option','jobinput') and
                          index in self.getkeys('option','jobinput', step) and
                          self.get('option','jobinput', step, index) != jobname):
                          # If we specify a different job as input to this task,
                          # we assume we are good to run it.
                          tasks_to_run[step+index] = []
                      else:
                          tasks_to_run[step+index] = inputs
  
                      processes[step+index] = multiprocessing.Process(target=self._runtask,
                                                                      args=(step, index, status))
  
  
              # We have to deinit the chip's logger before spawning the processes
              # since the logger object is not serializable. _runtask_safe will
              # reinitialize the logger in each new process, and we reinitialize
              # the primary chip's logger after the processes complete.
              self._deinit_logger()
  
              running_tasks = []
              while len(tasks_to_run) > 0 or len(running_tasks) > 0:
                  # Check for new tasks that can be launched.
                  for task, deps in list(tasks_to_run.items()):
                      # TODO: breakpoint logic:
                      # if task is bkpt, then don't launch while len(running_tasks) > 0
  
                      # Clear any tasks that have finished from dependency list.
                      for in_task in deps.copy():
                          if status[in_task] != TaskStatus.PENDING:
                              deps.remove(in_task)
  
                      # If there are no dependencies left, launch this task and
                      # remove from tasks_to_run.
                      if len(deps) == 0:
                          processes[task].start()
                          running_tasks.append(task)
                          del tasks_to_run[task]
  
                  # Check for situation where we have stuff left to run but don't
                  # have any tasks running. This shouldn't happen, but we will get
                  # stuck in an infinite loop if it does, so we want to break out
                  # with an explicit error.
                  if len(tasks_to_run) > 0 and len(running_tasks) == 0:
                      raise SiliconCompilerError('Tasks left to run, but no '
                          'running tasks. Steplist may be invalid.')
  
                  # Check for completed tasks.
                  # TODO: consider staying in this section of loop until a task
                  # actually completes.
                  for task in running_tasks.copy():
                      if not processes[task].is_alive():
                          running_tasks.remove(task)
                          if processes[task].exitcode > 0:
                              status[task] = TaskStatus.ERROR
                          else:
                              status[task] = TaskStatus.SUCCESS
  
                  # TODO: exponential back-off with max?
                  time.sleep(0.1)
  
              self._init_logger()
  
              # Make a clean exit if one of the steps failed
              for step in steplist:
                  index_succeeded = False
                  for index in indexlist[step]:
                      stepstr = step + index
                      if status[stepstr] != TaskStatus.ERROR:
                          index_succeeded = True
                          break
  
                  if not index_succeeded:
                      raise SiliconCompilerError('Run() failed, see previous errors.')
  
              # On success, write out status dict to flowgraph status'. We do this
              # since certain scenarios won't be caught by reading in manifests (a
              # failing step doesn't dump a manifest). For example, if the
              # steplist's final step has two indices and one fails.
              for step in steplist:
                  for index in indexlist[step]:
                      stepstr = step + index
                      if status[stepstr] != TaskStatus.PENDING:
                          self.set('flowgraph', flow, step, index, 'status', status[stepstr])
  
  
              # Merge cfg back from last executed runsteps.
              # Note: any information generated in steps that do not merge into the
              # last step will not be picked up in this chip object.
              # TODO: we might as well fix this? We can add a helper function to
              # find all steps in the steplist that don't lead to others.
  
              laststep = steplist[-1]
              for index in indexlist[laststep]:
                  lastdir = self._getworkdir(step=laststep, index=index)
  
                  # This no-op listdir operation is important for ensuring we have
                  # a consistent view of the filesystem when dealing with NFS.
                  # Without this, this thread is often unable to find the final
                  # manifest of runs performed on job schedulers, even if they
                  # completed successfully. Inspired by:
                  # https://stackoverflow.com/a/70029046.
  
                  os.listdir(os.path.dirname(lastdir))
  
                  lastcfg = f"{lastdir}/outputs/{self.get('design')}.pkg.json"
                  if status[laststep+index] == TaskStatus.SUCCESS:
                      self._read_manifest(lastcfg, clobber=False, partial=True)
                  else:
                      self.set('flowgraph', flow, laststep, index, 'status', TaskStatus.ERROR)
  
          # Clear scratchpad args since these are checked on run() entry
          self.set('arg', 'step', None, clobber=True)
          self.set('arg', 'index', None, clobber=True)
  
          # Store run in history
          self.record_history()
  
          # Storing manifest in job root directory
          filepath =  os.path.join(self._getworkdir(),f"{self.get('design')}.pkg.json")
          self.write_manifest(filepath)
  
      ##########################################################################
      def record_history(self):
          '''
          Copies all non-empty parameters from current job into the history
          dictionary.
          '''
  
          # initialize new dict
          jobname = self.get('option','jobname')
          self.cfg['history'][jobname] = {}
  
          # copy in all empty values of scope job
          allkeys = self.getkeys()
          for key in allkeys:
              # ignore history in case of cumulative history
              if key[0] != 'history':
                  scope = self.get(*key, field='scope')
                  if not self._keypath_empty(key) and (scope == 'job'):
                      self._copyparam(self.cfg,
                                      self.cfg['history'][jobname],
                                      key)
  
      ###########################################################################
      def _copyparam(self, cfgsrc, cfgdst, keypath):
          '''
          Copies a parameter into the manifest history dictionary.
          '''
  
          # 1. decend keypath, pop each key as its used
          # 2. create key if missing in destination dict
          # 3. populate leaf cell when keypath empty
          if keypath:
              key = keypath[0]
              keypath.pop(0)
              if key not in cfgdst.keys():
                  cfgdst[key] = {}
              self._copyparam(cfgsrc[key], cfgdst[key], keypath)
          else:
              for key in cfgsrc.keys():
                  if key not in ('example', 'switch', 'help'):
                      cfgdst[key] = copy.deepcopy(cfgsrc[key])
  
  
      ###########################################################################
      def show(self, filename=None, extra_options=None):
          '''
          Opens a graphical viewer for the filename provided.
  
          The show function opens the filename specified using a viewer tool
          selected based on the file suffix and the 'showtool' schema setup.
          The 'showtool' parameter binds tools with file suffixes, enabling the
          automated dynamic loading of tool setup functions from
          siliconcompiler.tools.<tool>/<tool>.py. Display settings and
          technology settings for viewing the file are read from the
          in-memory chip object schema settings. All temporary render and
          display files are saved in the <build_dir>/_show directory.
  
          The show() command can also be used to display content from an SC
          schema .json filename provided. In this case, the SC schema is
          converted to html and displayed as a 'dashboard' in the browser.
  
          Filenames with .gz and .zip extensions are automatically unpacked
          before being displayed.
  
          Args:
              filename: Name of file to display
  
          Examples:
              >>> show('build/oh_add/job0/export/0/outputs/oh_add.gds')
              Displays gds file with a viewer assigned by 'showtool'
              >>> show('build/oh_add/job0/export/0/outputs/oh_add.pkg.json')
              Displays manifest in the browser
          '''
  
          if extra_options is None:
              extra_options = []
  
          # Finding last layout if no argument specified
          if filename is None:
              self.logger.info('Searching build directory for layout to show.')
              design = self.get('design')
              # TODO: consider a more flexible approach here. I tried doing a
              # reverse search through all steps, but when verification steps are
              # enabled this finds a DEF passed into LVS rather than the GDS
              # Perhaps we could have a way for flows to register their "final"
              # output.
              laststep = 'export'
              lastindex = '0'
              lastdir = self._getworkdir(step=laststep, index=lastindex)
              gds_file= f"{lastdir}/outputs/{design}.gds"
              def_file = f"{lastdir}/outputs/{design}.def"
              if os.path.isfile(gds_file):
                  filename = gds_file
              elif os.path.isfile(def_file):
                  filename = def_file
  
          if filename is None:
              self.logger.error('Unable to automatically find layout in build directory.')
              self.logger.error('Try passing in a full path to show() instead.')
              return 1
  
          self.logger.info('Showing file %s', filename)
  
          # Parsing filepath
          filepath = os.path.abspath(filename)
          basename = os.path.basename(filepath)
          localfile = basename.replace(".gz","")
          filetype = os.path.splitext(localfile)[1].lower().replace(".","")
  
          #Check that file exists
          if not os.path.isfile(filepath):
              self.logger.error(f"Invalid filepath {filepath}.")
              return 1
  
          # Opening file from temp directory
          cwd = os.getcwd()
          showdir = self.get('option','builddir') + "/_show"
          os.makedirs(showdir, exist_ok=True)
          os.chdir(showdir)
  
          # Uncompress file if necessary
          if os.path.splitext(filepath)[1].lower() == ".gz":
              with gzip.open(filepath, 'rb') as f_in:
                  with open(localfile, 'wb') as f_out:
                      shutil.copyfileobj(f_in, f_out)
          else:
              shutil.copy(filepath, localfile)
  
          #Figure out which tool to use for opening data
          if filetype in self.getkeys('option','showtool'):
              # Using env variable and manifest to pass arguments
              os.environ['SC_FILENAME'] = localfile
              # Setting up tool
              tool = self.get('option','showtool', filetype)
              step = 'show'+filetype
              index = "0"
              self.set('arg', 'step', step)
              self.set('arg', 'index', index)
              setup_tool = self.find_function(tool, 'setup', 'tools')
              setup_tool(self, mode='show')
              self.write_manifest("sc_manifest.tcl", abspath=True)
              self.write_manifest("sc_manifest.json", abspath=True)
              self.set('arg', 'step', None)
              self.set('arg', 'index', None)
  
              exe = self._getexe(tool)
              if shutil.which(exe) is None:
                  self.logger.error(f'Executable {exe} not found.')
                  success = False
              else:
                  # Running command
                  cmdlist = self._makecmd(tool, step, index, extra_options=extra_options)
                  proc = subprocess.run(cmdlist)
                  success = proc.returncode == 0
          else:
              self.logger.error(f"Filetype '{filetype}' not set up in 'showtool' parameter.")
              success = False
  
          # Returning to original directory
          os.chdir(cwd)
          return success
  
      def read_lef(self, path, pdkname, stackup):
          '''Reads tech LEF and imports data into schema.
  
          This function reads layer information from a provided tech LEF and uses
          it to fill out the 'pdk', <pdkname>, 'grid' keypaths of the current chip
          object.
  
          Args:
              path (str): Path to LEF file.
              pdkname (str): Name of PDK associated with LEF file.
              stackup (str): Stackup associated with LEF file.
          '''
          data = leflib.parse(path)
          layer_index = 1
          for name, layer in data['layers'].items():
              if layer['type'] != 'ROUTING':
                  # Skip non-routing layers
                  continue
  
              sc_name = f'm{layer_index}'
              layer_index += 1
              self.set('pdk', pdkname, 'grid', stackup, name, 'name', sc_name)
  
              direction = None
              if 'direction' in layer:
                  direction = layer['direction'].lower()
                  self.set('pdk', pdkname, 'grid', stackup, name, 'dir', direction)
  
              if 'offset' in layer:
                  offset = layer['offset']
                  if isinstance(offset, float):
                      # Per LEF spec, a single offset value applies to the
                      # preferred routing direction. If one doesn't exist, we'll
                      # just ignore.
                      if direction == 'vertical':
                          self.set('pdk', pdkname, 'grid', stackup, name, 'xoffset', offset)
                      elif direction == 'horizontal':
                          self.set('pdk', pdkname, 'grid', stackup, name, 'yoffset', offset)
                  else:
                      xoffset, yoffset = offset
                      self.set('pdk', pdkname, 'grid', stackup, name, 'xoffset', xoffset)
                      self.set('pdk', pdkname, 'grid', stackup, name, 'yoffset', yoffset)
  
              if 'pitch' in layer:
                  pitch = layer['pitch']
                  if isinstance(pitch, float):
                      # Per LEF spec, a single pitch value applies to both
                      # directions.
                      self.set('pdk', pdkname, 'grid', stackup, name, 'xpitch', pitch)
                      self.set('pdk', pdkname, 'grid', stackup, name, 'ypitch', pitch)
                  else:
                      xpitch, ypitch = pitch
                      self.set('pdk', pdkname, 'grid', stackup, name, 'xpitch', xpitch)
                      self.set('pdk', pdkname, 'grid', stackup, name, 'ypitch', ypitch)
  
      ############################################################################
      # Chip helper Functions
      ############################################################################
      def _typecheck(self, cfg, leafkey, value):
          ''' Schema type checking
          '''
          ok = True
          valuetype = type(value)
          errormsg = ""
          if (not re.match(r'\[',cfg['type'])) & (valuetype==list):
              errormsg = "Value must be scalar."
              ok = False
              # Iterate over list
          else:
              # Create list for iteration
              if valuetype == list:
                  valuelist = value
              else:
                  valuelist = [value]
                  # Make type python compatible
              cfgtype = re.sub(r'[\[\]]', '', cfg['type'])
              for item in valuelist:
                  valuetype =  type(item)
                  if ((cfgtype != valuetype.__name__) and (item is not None)):
                      tupletype = re.match(r'\([\w\,]+\)',cfgtype)
                      #TODO: check tuples!
                      if tupletype:
                          pass
                      elif cfgtype == 'bool':
                          if not item in ['true', 'false']:
                              errormsg = "Valid boolean values are True/False/'true'/'false'"
                              ok = False
                      elif cfgtype == 'file':
                          pass
                      elif cfgtype == 'dir':
                          pass
                      elif (cfgtype == 'float'):
                          try:
                              float(item)
                          except:
                              errormsg = "Type mismatch. Cannot cast item to float."
                              ok = False
                      elif (cfgtype == 'int'):
                          try:
                              int(item)
                          except:
                              errormsg = "Type mismatch. Cannot cast item to int."
                              ok = False
                      elif item is not None:
                          errormsg = "Type mismach."
                          ok = False
  
          # Logger message
          if type(value) == list:
              printvalue = ','.join(map(str, value))
          else:
              printvalue = str(value)
          errormsg = (errormsg +
                      " Key=" + str(leafkey) +
                      ", Expected Type=" + cfg['type'] +
                      ", Entered Type=" + valuetype.__name__ +
                      ", Value=" + printvalue)
  
  
          return (ok, errormsg)
  
      #######################################
      def _getexe(self, tool):
          path = self.get('tool', tool, 'path')
          exe = self.get('tool', tool, 'exe')
          if exe is None:
              return None
  
          syspath = os.getenv('PATH', os.defpath)
          if path:
              # Prepend 'path' schema var to system path
              syspath = self._resolve_env_vars(path) + os.pathsep + syspath
  
          fullexe = shutil.which(exe, path=syspath)
  
          return fullexe
  
      #######################################
      def _makecmd(self, tool, step, index, extra_options=None):
          '''
          Constructs a subprocess run command based on eda tool setup.
          Creates a replay script in current directory.
          '''
  
          fullexe = self._getexe(tool)
  
          options = []
          is_posix = (sys.platform != 'win32')
  
          for option in self.get('tool', tool, 'option', step, index):
              options.extend(shlex.split(option, posix=is_posix))
  
          # Add scripts files
          if self.valid('tool', tool, 'script', step, index):
              scripts = self.find_files('tool', tool, 'script', step, index)
          else:
              scripts = []
  
          cmdlist = [fullexe]
          if extra_options:
              cmdlist.extend(extra_options)
          cmdlist.extend(options)
          cmdlist.extend(scripts)
          runtime_options = self.find_function(tool, 'runtime_options', 'tools')
          if runtime_options:
              for option in runtime_options(self):
                  cmdlist.extend(shlex.split(option, posix=is_posix))
  
          envvars = {}
          for key in self.getkeys('option','env'):
              envvars[key] = self.get('option','env', key)
          for item in self.getkeys('tool', tool, 'licenseserver'):
              license_file = self.get('tool', tool, 'licenseserver', item)
              if license_file:
                  envvars[item] = ':'.join(license_file)
          if self.get('tool', tool, 'path'):
              envvars['PATH'] = self.get('tool', tool, 'path') + os.pathsep + '$PATH'
          if (step in self.getkeys('tool', tool, 'env') and
              index in self.getkeys('tool', tool, 'env', step)):
              for key in self.getkeys('tool', tool, 'env', step, index):
                  envvars[key] = self.get('tool', tool, 'env', step, index, key)
  
          #create replay file
          script_name = 'replay.sh'
          with open(script_name, 'w') as f:
              print('#!/bin/bash', file=f)
  
              envvar_cmd = 'export'
              for key, val in envvars.items():
                  print(f'{envvar_cmd} {key}={val}', file=f)
  
              replay_cmdlist = [os.path.basename(cmdlist[0])] + cmdlist[1:]
              print(' '.join(f'"{arg}"' if ' ' in arg else arg for arg in replay_cmdlist), file=f)
          os.chmod(script_name, 0o755)
  
          return cmdlist
  
      #######################################
      def _get_cloud_region(self):
          # TODO: add logic to figure out if we're running on a remote cluster and
          # extract the region in a provider-specific way.
          return 'local'
  
      #######################################
      def _make_record(self, step, index, start, end, toolversion, toolpath, cli_args):
          '''
          Records provenance details for a runstep.
          '''
          self.set('record', step, index, 'scversion', _metadata.version)
  
          start_date = datetime.datetime.fromtimestamp(start).strftime('%Y-%m-%d %H:%M:%S')
          end_date = datetime.datetime.fromtimestamp(end).strftime('%Y-%m-%d %H:%M:%S')
  
          userid = getpass.getuser()
          self.set('record', step, index, 'userid', userid)
  
          if toolversion:
              self.set('record', step, index, 'toolversion', toolversion)
  
          self.set('record', step, index, 'starttime', start_date)
          self.set('record', step, index, 'endtime', end_date)
  
          machine = platform.node()
          self.set('record', step, index, 'machine', machine)
  
          self.set('record', step, index, 'region', self._get_cloud_region())
  
          try:
              gateways = netifaces.gateways()
              ipaddr, interface = gateways['default'][netifaces.AF_INET]
              macaddr = netifaces.ifaddresses(interface)[netifaces.AF_LINK][0]['addr']
              self.set('record', step, index, 'ipaddr', ipaddr)
              self.set('record', step, index, 'macaddr', macaddr)
          except KeyError:
              self.logger.warning('Could not find default network interface info')
  
          system = platform.system()
          if system == 'Darwin':
              lower_sys_name = 'macos'
          else:
              lower_sys_name = system.lower()
          self.set('record', step, index, 'platform', lower_sys_name)
  
          if system == 'Linux':
              distro_name = distro.id()
              self.set('record', step, index, 'distro', distro_name)
  
          if system == 'Darwin':
              osversion, _, _ = platform.mac_ver()
          elif system == 'Linux':
              osversion = distro.version()
          else:
              osversion = platform.release()
          self.set('record', step, index, 'osversion', osversion)
  
          if system == 'Linux':
              kernelversion = platform.release()
          elif system == 'Windows':
              kernelversion = platform.version()
          elif system == 'Darwin':
              kernelversion = platform.release()
          else:
              kernelversion = None
          if kernelversion:
              self.set('record', step, index, 'kernelversion', kernelversion)
  
          arch = platform.machine()
          self.set('record', step, index, 'arch', arch)
  
          self.set('record', step, index, 'toolpath', toolpath)
  
          toolargs = ' '.join(f'"{arg}"' if ' ' in arg else arg for arg in cli_args)
          self.set('record', step, index, 'toolargs', toolargs)
  
      #######################################
      def _safecompare(self, value, op, goal):
          # supported relational oprations
          # >, >=, <=, <. ==, !=
          if op == ">":
              return(bool(value>goal))
          elif op == ">=":
              return(bool(value>=goal))
          elif op == "<":
              return(bool(value<goal))
          elif op == "<=":
              return(bool(value<=goal))
          elif op == "==":
              return(bool(value==goal))
          elif op == "!=":
              return(bool(value!=goal))
          else:
              self.error = 1
              self.logger.error(f"Illegal comparison operation {op}")
  
  
      #######################################
      def _getworkdir(self, jobname=None, step=None, index='0'):
          '''Create a step directory with absolute path
          '''
  
          if jobname is None:
              jobname = self.get('option','jobname')
  
          dirlist =[self.cwd,
                    self.get('option','builddir'),
                    self.get('design'),
                    jobname]
  
          # Return jobdirectory if no step defined
          # Return index 0 by default
          if step is not None:
              dirlist.append(step)
              dirlist.append(index)
  
          return os.path.join(*dirlist)
  
      #######################################
      def _resolve_env_vars(self, filepath):
          resolved_path = os.path.expandvars(filepath)
  
          # variables that don't exist in environment get ignored by `expandvars`,
          # but we can do our own error checking to ensure this doesn't result in
          # silent bugs
          envvars = re.findall(r'\$(\w+)', resolved_path)
          for var in envvars:
              self.logger.warning(f'Variable {var} in {filepath} not defined in environment')
  
          return resolved_path
  
      #######################################
      def _get_imported_filename(self, pathstr):
          ''' Utility to map collected file to an unambigious name based on its path.
  
          The mapping looks like:
          path/to/file.ext => file_<md5('path/to/file.ext')>.ext
          '''
          path = pathlib.Path(pathstr)
          ext = ''.join(path.suffixes)
  
          # strip off all file suffixes to get just the bare name
          while path.suffix:
              path = pathlib.Path(path.stem)
          filename = str(path)
  
          pathhash = hashlib.sha1(pathstr.encode('utf-8')).hexdigest()
  
          return f'{filename}_{pathhash}{ext}'
  
      def _check_version(self, reported_version, tool):
          # Based on regex for deprecated "legacy specifier" from PyPA packaging
          # library. Use this to parse PEP-440ish specifiers with arbitrary
          # versions.
          _regex_str = r"""
              (?P<operator>(==|!=|<=|>=|<|>|~=))
              \s*
              (?P<version>
                  [^,;\s)]* # Since this is a "legacy" specifier, and the version
                            # string can be just about anything, we match everything
                            # except for whitespace, a semi-colon for marker support,
                            # a closing paren since versions can be enclosed in
                            # them, and a comma since it's a version separator.
              )
              """
          _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
  
          normalize_version = self.find_function(tool, 'normalize_version', 'tools')
          # Version is good if it matches any of the specifier sets in this list.
          spec_sets = self.get('tool', tool, 'version')
  
          for spec_set in spec_sets:
              split_specs = [s.strip() for s in spec_set.split(",") if s.strip()]
              specs_list = []
              for spec in split_specs:
                  match = re.match(_regex, spec)
                  if match is None:
                      self.logger.warning(f'Invalid version specifier {spec}. Defaulting to =={spec}.')
                      operator = '=='
                      spec_version = spec
                  else:
                      operator = match.group('operator')
                      spec_version = match.group('version')
                  specs_list.append((operator, spec_version))
  
              if normalize_version is None:
                  normalized_version = reported_version
                  normalized_specs = ','.join([f'{op}{ver}' for op, ver in specs_list])
              else:
                  normalized_version = normalize_version(reported_version)
                  normalized_specs = ','.join([f'{op}{normalize_version(ver)}' for op, ver in specs_list])
  
              try:
                  version = packaging.version.Version(normalized_version)
              except packaging.version.InvalidVersion:
                  self.logger.error(f'Version {reported_version} reported by {tool} does not match standard.')
                  if normalize_version is None:
                      self.logger.error('Tool driver should implement normalize_version().')
                  else:
                      self.logger.error(f'normalize_version() returned invalid version {normalized_version}')
  
                  return False
  
              try:
                  spec_set = packaging.specifiers.SpecifierSet(normalized_specs)
              except packaging.specifiers.InvalidSpecifier:
                  self.logger.error(f'Version specifier set {normalized_specs} does not match standard.')
                  return False
  
              if version in spec_set:
                  return True
  
          allowedstr = '; '.join(spec_sets)
          self.logger.error(f"Version check failed for {tool}. Check installation.")
          self.logger.error(f"Found version {reported_version}, did not satisfy any version specifier set {allowedstr}.")
          return False
  
  
  ###############################################################################
  # Package Customization classes
  ###############################################################################
  
  class YamlIndentDumper(yaml.Dumper):
      def increase_indent(self, flow=False, indentless=False):
          return super(YamlIndentDumper, self).increase_indent(flow, False)
  
  class SiliconCompilerError(Exception):
      ''' Minimal Exception wrapper used to raise sc runtime errors.
      '''
      def __init__(self, message):
          super(Exception, self).__init__(message)
  
files: siliconcompiler/core.py
files: # Copyright 2020 Silicon Compiler Authors. All Rights Reserved.
  
  from siliconcompiler import utils
  
  import re
  import os
  import sys
  import copy as pycopy
  import json
  
  SCHEMA_VERSION = '0.9.0'
  
  #############################################################################
  # PARAM DEFINITION
  #############################################################################
  
  def scparam(cfg,
              keypath,
              sctype=None,
              require=None,
              defvalue=None,
              scope='job',
              copy='false',
              lock='false',
              hashalgo='sha256',
              signature=None,
              notes=None,
              unit=None,
              shorthelp=None,
              switch=None,
              example=None,
              schelp=None):
  
      # 1. decend keypath until done
      # 2. create key if missing
      # 3. populate leaf cell when keypath empty
      if keypath:
          key = keypath[0]
          keypath.pop(0)
          if key not in cfg.keys():
              cfg[key] = {}
          scparam(cfg[key],
                  keypath,
                  sctype=sctype,
                  scope=scope,
                  require=require,
                  defvalue=defvalue,
                  copy=copy,
                  lock=lock,
                  hashalgo=hashalgo,
                  signature=signature,
                  notes=notes,
                  unit=unit,
                  shorthelp=shorthelp,
                  switch=switch,
                  example=example,
                  schelp=schelp)
      else:
  
          # removing leading spaces as if schelp were a docstring
          schelp = utils.trim(schelp)
  
          # setting valus based on types
          # note (bools are never lists)
          if re.match(r'bool',sctype):
              require = 'all'
              if defvalue is None:
                  defvalue = 'false'
          if re.match(r'\[',sctype) and signature is None:
              signature = []
          if re.match(r'\[',sctype) and defvalue is None:
              defvalue = []
  
          # mandatory for all
          cfg['defvalue'] = defvalue
          cfg['value'] = pycopy.copy(defvalue)
          cfg['type'] = sctype
          cfg['scope'] = scope
          cfg['require'] = require
          cfg['lock'] = lock
          cfg['switch'] = switch
          cfg['shorthelp'] = shorthelp
          cfg['example'] = example
          cfg['help'] = schelp
          cfg['signature'] = signature
          cfg['notes'] = notes
  
          # unit for floats/ints
          if unit is not None:
              cfg['unit'] = unit
  
          # file only values
          if re.search(r'file',sctype):
              cfg['hashalgo'] = hashalgo
              cfg['copy'] = copy
              cfg['filehash'] = []
              cfg['date'] = []
              cfg['author'] = []
  
  
  #############################################################################
  # CHIP CONFIGURATION
  #############################################################################
  
  def schema_cfg():
      '''Method for defining Chip configuration schema
      All the keys defined in this dictionary are reserved words.
      '''
  
      # SC version number (bump on every non trivial change)
      # Version number following semver standard.
      # Software version syncs with SC releases (from _metadata)
  
      SCHEMA_VERSION = '0.8.0'
  
      # Basic schema setup
      cfg = {}
  
      # Place holder dictionaries updated by core methods()
      cfg['history'] = {}
      cfg['library'] = {}
  
      scparam(cfg,['schemaversion'],
              sctype='str',
              scope='global',
              defvalue=SCHEMA_VERSION,
              require='all',
              shorthelp="Schema version number",
              lock='true',
              switch="-schemaversion <str>",
              example=["api: chip.get('schemaversion')"],
              schelp="""SiliconCompiler schema version number.""")
  
      # Design topmodule/entrypoint
      scparam(cfg,['design'],
              sctype='str',
              scope='global',
              require='all',
              shorthelp="Design top module name",
              switch="-design <str>",
              example=["cli: -design hello_world",
                      "api: chip.set('design', 'hello_world')"],
              schelp="""Name of the top level module or library. Required for all
              chip objects.""")
  
      # input/output
      io = {'input': ['Input','true'],
            'output': ['Output','false']
      }
  
      filetype = 'default'
      for item, val in io.items():
          scparam(cfg,[item, filetype],
                  sctype='[file]',
                  copy=f"{val[1]}",
                  shorthelp=f"{val[0]} files",
                  switch=f"-{item} 'filetype <file>'",
                  example=[
                      f"cli: -{item} 'verilog hello_world.v'",
                      f"api: chip.set({item},'verilog','hello_world.v')"],
                  schelp=f"""
                  List of {item} files specifed by type. The filetype name must
                  align with the parameter names within the flow and tool setup
                  scripts. Examples of acceptable file types include python, c,
                  systemc, verilog, vhdl, netlist, def, gds, gerber, saif, sdc,
                  saif, vcd, spef, sdf.""")
  
      # Inputs and constraints
      cfg = schema_constraint(cfg)
  
      # Options
      cfg = schema_option(cfg)
      cfg = schema_arg(cfg)
      cfg = schema_unit(cfg)
  
      # Technology configuration
      cfg = schema_fpga(cfg)
      cfg = schema_asic(cfg)
      cfg = schema_pdk(cfg)
  
      # Flows
      cfg = schema_tool(cfg)
      cfg = schema_flowgraph(cfg)
  
      # Metrics
      cfg = schema_checklist(cfg)
      cfg = schema_metric(cfg)
      cfg = schema_record(cfg)
  
      # Modeling
      cfg = schema_model(cfg)
  
      # Datasheeet
      cfg = schema_datasheet(cfg)
  
      # Packaging
      cfg = schema_package(cfg)
  
      return cfg
  
  ###############################################################################
  # FPGA
  ###############################################################################
  
  def schema_fpga(cfg):
      ''' FPGA configuration
      '''
  
      scparam(cfg,['fpga', 'arch'],
              sctype='[file]',
              copy='true',
              shorthelp="FPGA: architecture file",
              switch="-fpga_arch <file>",
              example=["cli: -fpga_arch myfpga.xml",
                       "api:  chip.set('fpga', 'arch', 'myfpga.xml')"],
              schelp=""" Architecture definition file for FPGA place and route
              tool. For the VPR tool, the file is a required XML based description,
              allowing targeting a large number of virtual and commercial
              architectures. For most commercial tools, the fpga part name provides
              enough information to enable compilation and the 'arch' parameter is
              optional.""")
  
      scparam(cfg,['fpga', 'vendor'],
              sctype='str',
              shorthelp="FPGA: vendor name",
              switch="-fpga_vendor <str>",
              example=["cli: -fpga_vendor acme",
                      "api:  chip.set('fpga', 'vendor', 'acme')"],
              schelp="""
              Name of the FPGA vendor. The parameter is used to check part
              name and to select the eda tool flow in case 'edaflow' is
              unspecified.""")
  
      scparam(cfg,['fpga', 'partname'],
              sctype='str',
              require='fpga',
              shorthelp="FPGA: part name",
              switch="-fpga_partname <str>",
              example=["cli: -fpga_partname fpga64k",
                       "api:  chip.set('fpga', 'partname', 'fpga64k')"],
              schelp="""
              Complete part name used as a device target by the FPGA compilation
              tool. The part name must be an exact string match to the partname
              hard coded within the FPGA eda tool.""")
  
      scparam(cfg,['fpga', 'board'],
              sctype='str',
              shorthelp="FPGA: board name",
              switch="-fpga_board <str>",
              example=["cli: -fpga_board parallella",
                       "api:  chip.set('fpga', 'board', 'parallella')"],
              schelp="""
              Complete board name used as a device target by the FPGA compilation
              tool. The board name must be an exact string match to the partname
              hard coded within the FPGA eda tool. The parameter is optional and can
              be used in place of a partname and pin constraints for some tools.""")
  
      scparam(cfg,['fpga', 'program'],
              sctype='bool',
              shorthelp="FPGA: program enable",
              switch="-fpga_program <bool>",
              example=["cli: -fpga_program",
                       "api:  chip.set('fpga', 'program', True)"],
              schelp="""Specifies that the bitstream should be loaded into an FPGA.""")
  
      scparam(cfg,['fpga', 'flash'],
              sctype='bool',
              shorthelp="FPGA: flash enable",
              switch="-fpga_flash <bool>",
              example=["cli: -fpga_flash",
                       "api:  chip.set('fpga', 'flash', True)"],
              schelp="""Specifies that the bitstream should be flashed in the board/device.
              The default is to load the bitstream into volatile memory (SRAM).""")
  
      return cfg
  
  ###############################################################################
  # PDK
  ###############################################################################
  
  def schema_pdk(cfg, stackup='default'):
      ''' Process design kit configuration
      '''
  
      tool = 'default'
      filetype = 'default'
      pdkname = 'default'
  
      scparam(cfg, ['pdk', pdkname, 'foundry'],
              sctype='str',
              scope='global',
              require="asic",
              shorthelp="PDK: foundry name",
              switch="-pdk_foundry 'pdkname <str>'",
              example=["cli: -pdk_foundry 'asap7 virtual'",
                      "api:  chip.set('pdk', 'asap7', 'foundry', 'virtual')"],
              schelp="""
              Name of foundry corporation. Examples include intel, gf, tsmc,
              samsung, skywater, virtual. The \'virtual\' keyword is reserved for
              simulated non-manufacturable processes.""")
  
      scparam(cfg, ['pdk', pdkname, 'node'],
              sctype='float',
              scope='global',
              require="asic",
              shorthelp="PDK: process node",
              switch="-pdk_node 'pdkname <float>'",
              example=["cli: -pdk_node 'asap7 130'",
                      "api:  chip.set('pdk', 'asap7', 'node', 130)"],
              schelp="""
              Approximate relative minimum dimension of the process target specified
              in nanometers. The parameter is required for flows and tools that
              leverage the value to drive technology dependent synthesis and APR
              optimization. Node examples include 180, 130, 90, 65, 45, 32, 22 14,
              10, 7, 5, 3.""")
  
      scparam(cfg, ['pdk', pdkname, 'version'],
              sctype='str',
              scope='global',
              shorthelp="PDK: version",
              switch="-pdk_version 'pdkname <str>'",
              example=["cli: -pdk_version 'asap7 1.0'",
                      "api:  chip.set('pdk', 'asap7', 'version', '1.0')"],
              schelp="""
              Alphanumeric string specifying the version of the PDK. Verification of
              correct PDK and IP versions is a hard ASIC tapeout require in all
              commercial foundries. The version number can be used for design manifest
              tracking and tapeout checklists.""")
  
      scparam(cfg, ['pdk', pdkname, 'stackup'],
              sctype='[str]',
              scope='global',
              require='asic',
              shorthelp="PDK: metal stackups",
              switch="-pdk_stackup 'pdkname <str>'",
              example=["cli: -pdk_stackup 'asap7 2MA4MB2MC'",
                       "api: chip.add('pdk', 'asap7','stackup','2MA4MB2MC')"],
              schelp="""
              List of all metal stackups offered in the process node. Older process
              nodes may only offer a single metal stackup, while advanced nodes
              offer a large but finite list of metal stacks with varying combinations
              of metal line pitches and thicknesses. Stackup naming is unique to a
              foundry, but is generally a long string or code. For example, a 10
              metal stackup with two 1x wide, four 2x wide, and 4x wide metals,
              might be identified as 2MA4MB2MC, where MA, MB, and MC denote wiring
              layers with different properties (thickness, width, space). Each
              stackup will come with its own set of routing technology files and
              parasitic models specified in the pdk_pexmodel and pdk_aprtech
              parameters.""")
  
      scparam(cfg, ['pdk', pdkname, 'thickness', stackup],
              sctype='float',
              scope='global',
              unit='mm',
              shorthelp="PDK: unit thickness",
              switch="-pdk_thickness 'pdkname stackup <float>'",
              example=["cli: -pdk_thickness 'asap7 2MA4MB2MC 1.57'",
                      "api:  chip.set('pdk', 'asap7', 'thickness', '2MA4MB2MC', 1.57)"],
              schelp="""
              Thickness of a manfuatured unit specified on a per stackup basis.""")
  
      scparam(cfg, ['pdk', pdkname, 'wafersize'],
              sctype='float',
              scope='global',
              unit='mm',
              require="asic",
              shorthelp="PDK: wafer size",
              switch="-pdk_wafersize 'pdkname <float>'",
              example=["cli: -pdk_wafersize 'asap7 300'",
                      "api:  chip.set('pdk', 'asap7', 'wafersize', 300)"],
              schelp="""
              Wafer diameter used in wafer based manufacturing process. The standard diameter
              for leading edge manufacturing is 300mm. For older process technologies
              and specialty fabs, smaller diameters such as 200, 100, 125, 100 are common.
              The value is used to calculate dies per wafer and full factory chip costs.""")
  
      scparam(cfg, ['pdk', pdkname, 'panelsize'],
              sctype='[(float,float)]',
              scope='global',
              unit='mm',
              shorthelp="PDK: panel size",
              switch="-pdk_panelsize 'pdkname <float>'",
              example=["cli: -pdk_panelsize 'asap7 (45.72,60.96)'",
                      "api:  chip.set('pdk', 'asap7', 'panelsize', (45.72,60.96))"],
              schelp="""
              List of panel sizes supported in the manufacturing process.
              """)
  
      scparam(cfg, ['pdk', pdkname, 'unitcost'],
              sctype='float',
              scope='global',
              unit='USD',
              shorthelp="PDK: unit cost",
              switch="-pdk_unitcost 'pdkname <float>'",
              example=["cli: -pdk_unitcost 'asap7 10000'",
                       "api:  chip.set('pdk', 'asap7', 'unitcost', 10000)"],
              schelp="""
              Raw cost per unit shipped by the factory, not accounting for yield
              loss.""")
  
      scparam(cfg, ['pdk', pdkname, 'd0'],
              sctype='float',
              scope='global',
              shorthelp="PDK: process defect density",
              switch="-pdk_d0 'pdkname <float>'",
              example=["cli: -pdk_d0 'asap7 0.1'",
                       "api:  chip.set('pdk', 'asap7', 'd0', 0.1)"],
              schelp="""
              Process defect density (d0) expressed as random defects per cm^2. The
              value is used to calculate yield losses as a function of area, which in
              turn affects the chip full factory costs. Two yield models are
              supported: Poisson (default), and Murphy. The Poisson based yield is
              calculated as dy = exp(-area * d0/100). The Murphy based yield is
              calculated as dy = ((1-exp(-area * d0/100))/(area * d0/100))^2.""")
  
      scparam(cfg, ['pdk', pdkname, 'hscribe'],
              sctype='float',
              scope='global',
              unit='mm',
              shorthelp="PDK: horizontal scribe line width",
              switch="-pdk_hscribe 'pdkname <float>'",
              example=["cli: -pdk_hscribe 'asap7 0.1'",
                       "api:  chip.set('pdk', 'asap7', 'hscribe', 0.1)"],
              schelp="""
              Width of the horizontal scribe line used during die separation.
              The process is generally completed using a mechanical saw, but can be
              done through combinations of mechanical saws, lasers, wafer thinning,
              and chemical etching in more advanced technologies. The value is used
              to calculate effective dies per wafer and full factory cost.""")
  
      scparam(cfg, ['pdk', pdkname, 'vscribe'],
              sctype='float',
              scope='global',
              unit='mm',
              shorthelp="PDK: vertical scribe line width",
              switch="-pdk_vscribe 'pdkname <float>'",
              example=["cli: -pdk_vscribe 'asap7 0.1'",
                       "api:  chip.set('pdk', 'asap7', 'vscribe', 0.1)"],
              schelp="""
               Width of the vertical scribe line used during die separation.
              The process is generally completed using a mechanical saw, but can be
              done through combinations of mechanical saws, lasers, wafer thinning,
              and chemical etching in more advanced technologies. The value is used
              to calculate effective dies per wafer and full factory cost.""")
  
      scparam(cfg, ['pdk', pdkname, 'edgemargin'],
              sctype='float',
              scope='global',
              unit='mm',
              shorthelp="PDK: wafer edge keep-out margin",
              switch="-pdk_edgemargin 'pdkname <float>'",
              example=[
                  "cli: -pdk_edgemargin 'asap7 1'",
                  "api:  chip.set('pdk', 'asap7', 'edgemargin', 1)"],
              schelp="""
              Keep-out distance/margin from the edge inwards. The edge
              is prone to chipping and need special treatment that preclude
              placement of designs in this area. The edge value is used to
              calculate effective units per wafer/panel and full factory cost.""")
  
      scparam(cfg, ['pdk', pdkname, 'density'],
              sctype='float',
              scope='global',
              shorthelp="PDK: transistor density",
              switch="-pdk_density 'pdkname <float>'",
              example=["cli: -pdk_density 'asap7 100e6'",
                      "api:  chip.set('pdk', 'asap7', 'density', 10e6)"],
              schelp="""
              Approximate logic density expressed as # transistors / mm^2
              calculated as:
              0.6 * (Nand2 Transistor Count) / (Nand2 Cell Area) +
              0.4 * (Register Transistor Count) / (Register Cell Area)
              The value is specified for a fixed standard cell library within a node
              and will differ depending on the library vendor, library track height
              and library type. The value can be used to to normalize the effective
              density reported for the design across different process nodes. The
              value can be derived from a variety of sources, including the PDK DRM,
              library LEFs, conference presentations, and public analysis.""")
  
      simtype = 'default'
      scparam(cfg, ['pdk', pdkname, 'devmodel', tool, simtype, stackup],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: device models",
              switch="-pdk_devmodel 'pdkname tool simtype stackup <file>'",
              example=[
              "cli: -pdk_devmodel 'asap7 xyce spice M10 asap7.sp'",
              "api: chip.set('pdk','asap7','devmodel','xyce','spice','M10','asap7.sp')"],
              schelp="""
              List of filepaths to PDK device models for different simulation
              purposes and for different tools. Examples of device model types
              include spice, aging, electromigration, radiation. An example of a
              'spice' tool is xyce. Device models are specified on a per metal stack
              basis. Process nodes with a single device model across all stacks will
              have a unique parameter record per metal stack pointing to the same
              device model file.  Device types and tools are dynamic entries
              that depend on the tool setup and device technology. Pseud-standardized
              device types include spice, em (electromigration), and aging.""")
  
      corner='default'
      scparam(cfg, ['pdk', pdkname, 'pexmodel', tool, stackup, corner],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: parasitic TCAD models",
              switch="-pdk_pexmodel 'pdkname tool stackup corner <file>'",
              example=[
                  "cli: -pdk_pexmodel 'asap7 fastcap M10 max wire.mod'",
                  "api: chip.set('pdk','asap7','pexmodel','fastcap','M10','max','wire.mod')"],
              schelp="""
              List of filepaths to PDK wire TCAD models used during automated
              synthesis, APR, and signoff verification. Pexmodels are specified on
              a per metal stack basis. Corner values depend on the process being
              used, but typically include nomenclature such as min, max, nominal.
              For exact names, refer to the DRM. Pexmodels are generally not
              standardized and specified on a per tool basis. An example of pexmodel
              type is 'fastcap'.""")
  
      src = 'default'
      dst = 'default'
      scparam(cfg, ['pdk', pdkname, 'layermap', tool, src, dst, stackup],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: layer map file",
              switch="-pdk_layermap 'pdkname tool src dst stackup <file>'",
              example=[
                  "cli: -pdk_layermap 'asap7 klayout db gds M10 asap7.map'",
                  "api: chip.set('pdk','asap7','layermap','klayout','db','gds','M10','asap7.map')"],
              schelp="""
              Files describing input/output mapping for streaming layout data from
              one format to another. A foundry PDK will include an official layer
              list for all user entered and generated layers supported in the GDS
              accepted by the foundry for processing, but there is no standardized
              layer definition format that can be read and written by all EDA tools.
              To ensure mask layer matching, key/value type mapping files are needed
              to convert EDA databases to/from GDS and to convert between different
              types of EDA databases. Layer maps are specified on a per metal
              stackup basis. The 'src' and 'dst' can be names of SC supported tools
              or file formats (like 'gds').""")
  
  
      scparam(cfg, ['pdk', pdkname, 'display', tool, stackup],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: display file",
              switch="-pdk_display 'pdkname tool stackup <file>'",
              example=[
                  "cli: -pdk_display 'asap7 klayout M10 display.lyt'",
                  "api: chip.set('pdk','asap7','display','klayout','M10','display.cfg')"],
              schelp="""
              Display configuration files describing colors and pattern schemes for
              all layers in the PDK. The display configuration file is entered on a
              stackup and tool basis.""")
  
      #TODO: create firm list of accepted files
      libarch = 'default'
      scparam(cfg, ['pdk', pdkname, 'aprtech', tool, stackup, libarch, filetype],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: APR technology files",
              switch="-pdk_aprtech 'pdkname tool stackup libarch filetype <file>'",
              example=[
                  "cli: -pdk_aprtech 'asap7 openroad M10 12t lef tech.lef'",
                  "api: chip.set('pdk','asap7','aprtech','openroad','M10','12t','lef','tech.lef')"],
              schelp="""
              Technology file containing setup information needed to enable DRC clean APR
              for the specified stackup, libarch, and format. The 'libarch' specifies the
              library architecture (e.g. library height). For example a PDK with support
              for 9 and 12 track libraries might have 'libarchs' called 9t and 12t.
              The standard filetype for specifying place and route design rules for a
              process node is through a 'lef' format technology file. The
              'filetype' used in the aprtech is used by the tool specific APR TCL scripts
              to set up the technology parameters. Some tools may require additional
              files beyond the tech.lef file. Examples of extra file types include
              antenna, tracks, tapcell, viarules, em.""")
  
      checks = ['lvs', 'drc', 'erc', 'fill']
      name = 'default'
      for item in checks:
          scparam(cfg, ['pdk', pdkname, item, 'runset', tool, stackup, name],
                  sctype='[file]',
                  scope='global',
                  shorthelp=f"PDK: {item.upper()} runset files",
                  switch=f"-pdk_{item}_runset 'pdkname tool stackup name <file>'",
                  example=[
                      f"cli: -pdk_{item}_runset 'asap7 magic M10 basic $PDK/{item}.rs'",
                      f"api: chip.set('pdk', 'asap7','{item}','runset','magic','M10','basic','$PDK/{item}.rs')"],
                  schelp=f"""Runset files for {item.upper()} task.""")
  
          scparam(cfg, ['pdk', pdkname, item, 'waiver', tool, stackup, name],
                  sctype='[file]',
                  scope='global',
                  shorthelp=f"PDK: {item.upper()} waiver files",
                  switch=f"-pdk_{item}_waiver 'tool stackup name <file>'",
                  example=[
                      f"cli: -pdk_{item}_waiver 'asap7 magic M10 basic $PDK/{item}.txt'",
                      f"api: chip.set('pdk', 'asap7','{item}','waiver','magic','M10','basic','$PDK/{item}.txt')"],
                  schelp=f"""Waiver files for {item.upper()} task.""")
  
      ################
      # Routing grid
      ################
  
      layer = 'default'
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'name'],
              sctype='str',
              scope='global',
              shorthelp="PDK: routing grid name map",
              switch="-pdk_grid_name 'pdkname stackup layer <str>'",
              example=[
                  "cli: -pdk_grid_name 'asap7 M10 metal1 m1'",
                  "api: chip.set('pdk','asap7','grid','M10','metal1','name','m1')"],
              schelp="""
              Maps PDK metal names to the SC standardized layer stack
              starting with m1 as the lowest routing layer and ending
              with m<n> as the highest routing layer. The map is
              specified on a per metal stack basis.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'dir'],
              sctype='str',
              scope='global',
              shorthelp="PDK: routing grid preferred direction",
              switch="-pdk_grid_dir 'pdkname stackup layer <str>'",
              example=[
                  "cli: -pdk_grid_dir 'asap7 M10 m1 horizontal'",
                  "api: chip.set('pdk','asap7','grid','M10','m1','dir','horizontal')"],
              schelp="""
              Preferred routing direction specified on a per stackup
              and per metal basis. Valid routing directions are horizontal
              and vertical.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'xpitch'],
              sctype='float',
              scope='global',
              unit='um',
              shorthelp="PDK: routing grid vertical wire pitch",
              switch="-pdk_grid_xpitch 'pdkname stackup layer <float>'",
              example= [
                  "cli: -pdk_grid_xpitch 'asap7 M10 m1 0.5'",
                  "api: chip.set('pdk', 'asap7','grid','M10','m1','xpitch','0.5')"],
              schelp="""
              Defines the routing pitch for vertical wires on a per stackup and
              per metal basis.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'ypitch'],
              sctype='float',
              scope='global',
              unit='um',
              shorthelp="PDK: routing grid horizontal wire pitch",
              switch="-pdk_grid_ypitch 'pdkname stackup layer <float>'",
              example= [
                  "cli: -pdk_grid_ypitch 'asap7 M10 m1 0.5'",
                  "api: chip.set('pdk','asap7','grid','M10','m1','ypitch','0.5')"],
              schelp="""
              Defines the routing pitch for horizontal wires on a per stackup and
              per metal basis.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'xoffset'],
              sctype='float',
              scope='global',
              unit='um',
              shorthelp="PDK: routing grid vertical wire offset",
              switch="-pdk_grid_xoffset 'pdkname stackup layer <float>'",
              example= [
                  "cli: -pdk_grid_xoffset 'asap7 M10 m2 0.5'",
                  "api: chip.set('pdk','asap7','grid','M10','m2','xoffset','0.5')"],
              schelp="""
              Defines the grid offset of a vertical metal layer specified on a per
              stackup and per metal basis.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'yoffset'],
              sctype='float',
              scope='global',
              unit='um',
              shorthelp="PDK: routing grid horizontal wire offset",
              switch="-pdk_grid_yoffset 'pdkname stackup layer <float>'",
              example= [
                  "cli: -pdk_grid_yoffset 'asap7 M10 m2 0.5'",
                  "api: chip.set('pdk', 'asap7','grid','M10','m2','yoffset','0.5')"],
              schelp="""
              Defines the grid offset of a horizontal metal layer specified on a per
              stackup and per metal basis.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'adj'],
              sctype='float',
              scope='global',
              shorthelp="PDK: routing grid resource adjustment",
              switch="-pdk_grid_adj 'pdkname stackup layer <float>'",
              example= [
                  "cli: -pdk_grid_adj 'asap7 M10 m2 0.5'",
                  "api: chip.set('pdk','asap7','grid','M10','m2','adj','0.5')"],
              schelp="""
              Defines the routing resources adjustments for the design on a per layer
              basis. The value is expressed as a fraction from 0 to 1. A value of
              0.5 reduces the routing resources by 50%. If not defined, 100%
              routing resource utilization is permitted.""")
  
      corner='default'
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'cap', corner],
              sctype='float',
              scope='global',
              unit='ff/um',
              shorthelp="PDK: routing grid unit capacitance",
              switch="-pdk_grid_cap 'pdkname stackup layer corner <float>''",
              example= [
                  "cli: -pdk_grid_cap 'asap7 M10 m2 fast 0.2'",
                  "api: chip.set('pdk','asap7','grid','M10','m2','cap','fast','0.2')"],
              schelp="""
              Unit capacitance of a wire defined by the grid width and spacing values
              in the 'grid' structure specified on a per
              stackup, metal, and corner basis. As a rough rule of thumb, this value
              tends to stay around 0.2ff/um. This number should only be used for
              reality confirmation. Accurate analysis should use the PEX models.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'res', corner],
              sctype='float',
              scope='global',
              unit='ohm/um',
              shorthelp="PDK: routing grid unit resistance",
              switch="-pdk_grid_res 'pdkname stackup layer corner <float>''",
              example= [
                  "cli: -pdk_grid_res 'asap7 M10 m2 fast 0.2'",
                  "api: chip.set('pdk','asap7','grid','M10','m2','res','fast','0.2')"],
              schelp="""
              Resistance of a wire defined by the grid width and spacing values
              in the 'grid' structure specified as ohms/um on a per
              stackup, metal, and corner basis. The parameter is only meant to be
              used as a sanity check and for coarse design planning. Accurate
              analysis should use the TCAD PEX models.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'tcr', corner],
              sctype='float',
              scope='global',
              unit='%/degree(T)',
              shorthelp="PDK: routing grid temperature coefficient",
              switch="-pdk_grid_tcr 'pdkname stackup layer corner <float>'",
              example= [
                  "cli: -pdk_grid_tcr 'asap7 M10 m2 fast 0.2'",
                  "api: chip.set('pdk','asap7','grid','M10','m2','tcr','fast','0.2')"],
              schelp="""
              Temperature coefficient of resistance of the wire defined by the grid
              width and spacing values in the 'grid' structure on a per stackup,
              layer, and corner basis. The number is only meant to be used as a
              sanity check and for coarse design planning. Accurate analysis
              should use the PEX models.""")
  
      ###############
      # EDA vars
      ###############
  
      key='default'
      scparam(cfg, ['pdk', pdkname, 'file', tool, key, stackup],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: special file",
              switch="-pdk_file 'pdkname tool key stackup <file>'",
              example=[
                  "cli: -pdk_file 'asap7 xyce spice M10 asap7.sp'",
                  "api: chip.set('pdk','asap7','file','xyce','spice','M10','asap7.sp')"],
              schelp="""
              List of named files specified on a per tool and per stackup basis.
              The parameter should only be used for specifying files that are
              not directly  supported by the SiliconCompiler PDK schema.""")
  
      scparam(cfg, ['pdk', pdkname, 'directory', tool, key, stackup],
              sctype='[dir]',
              scope='global',
              shorthelp="PDK: special directory",
              switch="-pdk_directory 'pdkname tool key stackup <file>'",
              example=[
                  "cli: -pdk_directory 'asap7 xyce rfmodel M10 rftechdir'",
                  "api: chip.set('pdk','asap7','directory','xyce','rfmodel','M10','rftechdir')"],
              schelp="""
              List of named directories specified on a per tool and per stackup basis.
              The parameter should only be used for specifying files that are
              not directly  supported by the SiliconCompiler PDK schema.""")
  
      scparam(cfg, ['pdk', pdkname, 'var', tool, key, stackup],
              sctype='[str]',
              scope='global',
              shorthelp="PDK: special variable",
              switch="-pdk_var 'pdkname tool stackup key <str>'",
              example=[
                  "cli: -pdk_var 'asap7 xyce modeltype M10 bsim4'""",
                  "api: chip.set('pdk','asap7','var','xyce','modeltype','M10','bsim4')"],
              schelp="""
              List of key/value strings specified on a per tool and per stackup basis.
              The parameter should only be used for specifying variables that are
              not directly  supported by the SiliconCompiler PDK schema.""")
  
      ###############
      # Docs
      ###############
  
      scparam(cfg,['pdk', pdkname, 'doc', 'homepage'],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: documentation homepage",
              switch="-pdk_doc_homepage 'pdkname <file>'",
              example=["cli: -pdk_doc_homepage 'asap7 index.html'",
                       "api: chip.set('pdk','asap7','doc','homepage','index.html')"],
              schelp="""
              Filepath to PDK docs homepage. Modern PDKs can include tens or
              hundreds of individual documents. A single html entry point can
              be used to present an organized documentation dashboard to the
              designer.""")
  
      doctypes = ['datasheet',
                  'reference',
                  'userguide',
                  'install',
                  'quickstart',
                  'releasenotes',
                  'tutorial']
  
      for item in doctypes:
          scparam(cfg,['pdk', pdkname, 'doc', item],
                  sctype='[file]',
                  scope='global',
                  shorthelp=f"PDK: {item}",
                  switch= f"-pdk_doc_{item} 'pdkname <file>'",
                  example=[f"cli: -pdk_doc_{item} 'asap7 {item}.pdf'",
                           f"api: chip.set('pdk','asap7','doc',{item},'{item}.pdf')"],
                  schelp=f"""Filepath to {item} document.""")
  
      return cfg
  
  ###############################################################################
  # Design Modeling
  ###############################################################################
  
  def schema_model(cfg):
  
      corner = 'default'
      filetype = 'default'
      stackup =  'default'
  
      # functional
      scparam(cfg,['model', 'functional', filetype],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: Functional",
              switch=f"-model_functional 'filetype <file>'",
              example=[
                  f"cli: -model_functional 'systemc adder.sc'",
                  f"api: chip.set('model','functional','systemc','adder.sc')"],
              schelp=f"""
              Filepaths to (fast) functional models specified on a per filetype
              basis.""")
  
      # formal
      scparam(cfg,['model', 'formal', filetype],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: Formal",
              switch=f"-model_formal 'filetype <file>'",
              example=[
                  f"cli: -model_formal 'smv adder.smv'",
                  f"api: chip.set('model','formal','smv','adder.smv')"],
              schelp=f"""
              Filepaths to formal models specified on a per filetype basis.""")
  
      # rtl
      scparam(cfg,['model', 'rtl', filetype],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: RTL",
              switch=f"-model_rtl 'filetype <file>'",
              example=[
                  f"cli: -model_rtl 'systemc adder.sc'",
                  f"api: chip.set('model','rtl','systemc','adder.sc')"],
              schelp=f"""
              Filepaths to cycle accurate model specified on a per filetype
              basis.""")
  
      # IO
      scparam(cfg,['model', 'io', filetype],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: IO",
              switch=f"-model_io 'filetype <file>'",
              example=[
                  f"cli: -model_io 'ibis top.ibs'",
                  f"api: chip.set('model','io','ibis','top.ibis')"],
              schelp=f"""
              Filepaths to IO models specified on a per filetype basis.""")
  
      # thermal
      scparam(cfg,['model', 'thermal', filetype],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: Thermal",
              switch=f"-model_thermal 'filetype corner <file>'",
              example=[
                  f"cli: -model_thermal 'delphi adder_thermal.sp'",
                  f"api: chip.set('model','thermal','delphi','adder_thermal.sp')"],
              schelp=f"""
              Filepaths to thermal models.""")
  
      # timing
      scparam(cfg,['model', 'timing', filetype, corner],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: Timing",
              switch=f"-model_timing 'filetype corner <file>'",
              example=[
                  f"cli: -model_timing 'nldm-libgz ss ss.lib.gz'",
                  f"api: chip.set('model','timing','nldm-ldb','ss','ss.ldb')"],
              schelp=f"""
              Filepaths to static timing models specified on a per filetype and
              per corner basis.  Examples of filetypes include: nldm, nldm-ldb,
              nldm-libgz, ccs, ccs-libgz, ccs-ldb, scm, scm-libgz, scm-ldb,
              aocv, aocv-libgz, aocv-ldb.""")
  
      # power
      scparam(cfg,['model', 'power', filetype, corner],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: Power",
              switch=f"-model_power 'filetype corner <file>'",
              example=[
                  f"cli: -model_power 'apl ss adder.ss.apl'",
                  f"api: chip.set('model','power','apl','ss','adder.ss.apl')"],
              schelp=f"""
              Filepaths to power/current models.""")
  
      # layout
      scparam(cfg,['model', 'layout', filetype, stackup],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: Layout",
              switch=f"-model_layout 'filetype stackup <file>'",
              example=[
                  f"cli: -model_layout 'lef 10M adder.lef'",
                  f"api: chip.set('model','layout','lef','10M','adder.lef')"],
              schelp=f"""
              Filepaths to abstract layout views specified on a per filetype
              and per stackup basis.""")
  
      return cfg
  
  ###############################################################################
  # Datasheet
  ###############################################################################
  
  def schema_datasheet(cfg, design='default', name='default', mode='default'):
  
      # Device Features
      scparam(cfg, ['datasheet', design, 'feature', name],
              sctype='float',
              shorthelp=f"Datasheet: device feature specification",
              switch=f"-datasheet_feature 'design name <float>'",
              example=[
                  f"cli: -datasheet_feature 'mydevice ram 64e6'",
                  f"api: chip.set('datasheet','mydevice','feature','ram', 1e9)"],
              schelp=f"""Quantity of a specified feature. The 'unit'
              field should be used to specify the units used when unclear.""")
  
      # Absolute max voltage
      scparam(cfg, ['datasheet', design, 'limits', 'voltage', name],
              sctype='(float,float)',
              shorthelp=f"Datasheet: absolute voltage limits",
              switch=f"-datasheet_limits_voltage 'design pin <(float,float)>'",
              example=[
                  f"cli: -datasheet_limits_voltage 'mydevice vdd (-0.4,1.1)'",
                  f"api: chip.set('datasheet','mydevice','limits','voltage','vdd', (-0.4,1.1))"],
              schelp=f"""Device absolute minimum/maximum voltage not to be
              exceeded, specified on a per pin basis.""")
  
      # Absolute max temperatures
      metrics = {'storagetemp': 'storage temperature limits',
                 'junctiontemp' :'junction temperature limits'}
  
      for item, val in metrics.items():
          scparam(cfg, ['datasheet', design, 'limits', item],
                  sctype='(float,float)',
                  shorthelp=f"Datasheet: absolute {val}",
                  switch=f"-datasheet_{item} 'design <(float,float)>'",
                  example=[
                      f"cli: -datasheet_{item} 'mydevice (-40,125)'",
                      f"api: chip.set('datasheet','mydevice','limits','{item}',(-40,125))"],
                  schelp=f"""Device absolute {val} not to be exceeded.""")
  
      # Package Pin Map
      package = 'default'
      scparam(cfg, ['datasheet', design, 'pin', name, 'map', package],
              sctype='str',
              shorthelp=f"Datasheet: package pin map",
              switch=f"-datasheet_pin_map 'design name package <str>'",
              example=[
                  f"cli: -datasheet_pin_map 'mydevice in0 bga512 B4'",
                  f"api: chip.set('datasheet','mydevice','pin','in0','map','bga512','B4')"],
              schelp=f"""Signal to package pin mapping specified on a per package basis.""")
  
      # Pin type
      scparam(cfg, ['datasheet', design, 'pin', name, 'type', mode],
              sctype='str',
              shorthelp=f"Datasheet: pin type",
              switch=f"-datasheet_pin_type 'design name mode <str>'",
              example=[
                  f"cli: -datasheet_pin_type 'mydevice vdd type power'",
                  f"api: chip.set('datasheet','mydevice','pin','vdd','type','global','power')"],
              schelp=f"""Pin type specified on a per mode basis. Acceptable pin types
              include: digital, analog, clk, power, ground""")
  
      # Pin direction
      scparam(cfg, ['datasheet', design, 'pin', name, 'dir', mode],
              sctype='str',
              shorthelp=f"Datasheet: pin direction",
              switch=f"-datasheet_pin_dir 'design name mode <str>'",
              example=[
                  f"cli: -datasheet_pin_dir 'mydevice clk global input'",
                  f"api: chip.set('datasheet','mydevice','pin','clk','dir','global','input')"],
              schelp=f"""Pin direction specified on a per mode basis. Acceptable pin
              directions include: input, output, inout.""")
  
      # Complementary pin (for differential pair)
      scparam(cfg, ['datasheet', design, 'pin', name, 'complement', mode],
              sctype='str',
              shorthelp=f"Datasheet: pin complement",
              switch=f"-datasheet_pin_complement 'design name mode <str>'",
              example=[
                  f"cli: -datasheet_pin_complement 'mydevice ina global inb'",
                  f"api: chip.set('datasheet','mydevice','pin','ina','complement','global','inb')"],
              schelp=f"""Pin complement specified on a per mode basis for differential
              signals.""")
  
      # Related clock
      scparam(cfg, ['datasheet', design, 'pin', name, 'clk', mode],
                  sctype='str',
                  shorthelp=f"Datasheet: pin related clock",
                  switch=f"-datasheet_pin_clk 'design name mode <str>'",
                  example=[
                      f"cli: -datasheet_pin_clk 'mydevice ina global clka'",
                      f"api: chip.set('datasheet','mydevice','pin','ina','clk','global','clka')"],
              schelp=f"""Pin related clock specified on a per mode basis.""")
  
      # Related supply
      scparam(cfg, ['datasheet', design, 'pin', name, 'supply', mode],
                  sctype='str',
                  shorthelp=f"Datasheet: pin related power supply",
                  switch=f"-datasheet_pin_supply 'design name mode <str>'",
                  example=[
                      f"cli: -datasheet_pin_supply 'mydevice ina global vdd'",
                      f"api: chip.set('datasheet','mydevice','pin','ina','supply','global','vdd')"],
              schelp=f"""Pin related power supply specified on a per mode basis.""")
  
      # Related ground
      scparam(cfg, ['datasheet', design, 'pin', name, 'ground', mode],
                  sctype='str',
                  shorthelp=f"Datasheet: pin related ground",
                  switch=f"-datasheet_pin_ground 'design name mode <str>'",
                  example=[
                      f"cli: -datasheet_pin_ground 'mydevice ina ground vss'",
                      f"api: chip.set('datasheet','mydevice','pin','ina','ground','global','vss')"],
              schelp=f"""Pin related ground rail specified on a per mode basis.""")
  
      # Standard
      scparam(cfg, ['datasheet', design, 'pin', name, 'standard', mode],
              sctype='[str]',
              shorthelp=f"Datasheet: pin standard",
              switch=f"-datasheet_pin_standard 'design name mode <str>'",
              example=[
                  f"cli: -datasheet_pin_standard 'mydevice ba0 global ddr4'",
                  f"api: chip.set('datasheet','mydevice','pin','ina','standard','global','ddr4')"],
              schelp=f"""Pin communication standard specified on a per mode basis.""")
  
      # Reset value
      scparam(cfg, ['datasheet', design, 'pin', name, 'resetvalue', mode],
              sctype='[str]',
              shorthelp=f"Datasheet: pin reset value",
              switch=f"-datasheet_pin_resetvalue 'design name mode <str>'",
              example=[
                  f"cli: -datasheet_pin_resetvalue 'mydevice clk global weak1'",
                  f"api: chip.set('datasheet','mydevice','pin','clk','resetvalue','global','weak1')"],
              schelp=f"""Pin reset value specified on a per mode basis. Legal reset
              values include weak1, weak0, strong0, strong1, highz.""")
  
      # DC levels
      metrics = {'vol': ['low output voltage level', (-0.2,0,0.2), 'V'],
                 'voh': ['high output voltage level', (4.6,4.8,5.2), 'V'],
                 'vil': ['low input voltage level', (-0.2, 0, 1.0), 'V'],
                 'vih': ['high input voltage level', (1.4, 1.8, 2.2), 'V'],
                 'vcm': ['common mode voltage', (0.3, 1.2, 1.6), 'V'],
                 'vdiff': ['differential voltage', (0.2, 0.3, 0.9), 'V'],
                 'vnoise': ['random voltage noise', (0,0.01,0.1), 'V'],
                 'vhbm': ['machine model ESD tolerance', (100, 300, 500), 'V'],
                 'vcdm': ['human body model ESD tolerance', (100,300,500), 'V'],
                 'rdiff': ['differential pair resistance', (45,50,55), 'ohm'],
                 'rpullup': ['pullup resistance', (1000, 1200, 3000), 'ohm'],
                 'rpulldown': ['pulldown resistance', (1000, 1200, 3000), 'ohm'],
                 'idrive': ['drive current', (10e-3, 12e-3, 15e-3), 'A'],
                 'iinject': ['injection current', (1e-3, 1.2e-3, 1.5e-3), 'A'],
                 'ileakage': ['leakage current', (1e-6, 1.2e-6, 1.5e-6), 'A'],
                 'capacitance': ['capacitance', (1e-12, 1.2e-12, 1.5e-12), 'F']}
  
      for item, val in metrics.items():
          scparam(cfg, ['datasheet', design, 'pin', name, item, mode],
                  unit=val[2],
                  sctype='(float,float,float)',
                  shorthelp=f"Datasheet: {val[0]}",
                  switch=f"-datasheet_pin_{item} 'design pin mode <(float,float,float)>'",
                  example=[
                      f"cli: -datasheet_pin_{item} 'mydevice sclk global {val[1]}'",
                      f"api: chip.set('datasheet','mydevice','pin','sclk','{item}','global',{val[1]}"],
                  schelp=f"""Pin {val[0]}. Values are tuples of (min, typical, max).""")
  
      # AC Timing
      metrics = {'tsetup': ['setup time', (1e-9, 2e-9, 4e-9), 's'],
                 'thold': ['hold time', (1e-9, 2e-9, 4e-9), 's'],
                 'trise': ['rise transition', (1e-9, 2e-9, 4e-9), 's'],
                 'tfall': ['fall transition', (1e-9, 2e-9, 4e-9), 's'],
                 'tperiod': ['minimum period', (1e-9, 2e-9, 4e-9), 's'],
                 'tpulse': ['pulse width', (1e-9, 2e-9, 4e-9), 's'],
                 'tjitter': ['rms jitter', (1e-9, 2e-9, 4e-9), 's'],
                 'dutycycle': ['duty cycle', (45, 50, 55), '%']}
  
      for item, val in metrics.items():
          scparam(cfg, ['datasheet', design, 'pin', name, item, mode],
                  unit=val[2],
                  sctype='(float,float,float)',
                  shorthelp=f"Datasheet: {val[0]}",
                  switch=f"-datasheet_pin_{item} 'design pin mode <(float,float,float)>'",
                  example=[
                      f"cli: -datasheet_pin_{item} 'mydevice sclk global {val[1]}'",
                      f"api: chip.set('datasheet','mydevice','pin','sclk','{item}','global',{val[1]}"],
                  schelp=f"""Pin {val[0]}. Values are tuples of (min, typical, max).""")
  
      return cfg
  
  ###############################################################################
  # Flow Configuration
  ###############################################################################
  
  def schema_flowgraph(cfg, flow='default', step='default', index='default'):
  
      # flowgraph input
      scparam(cfg,['flowgraph', flow, step, index, 'input'],
              sctype='[(str,str)]',
              shorthelp="Flowgraph: step input",
              switch="-flowgraph_input 'flow step index <(str,str)>'",
              example=[
                  "cli: -flowgraph_input 'asicflow cts 0 (place,0)'",
                  "api:  chip.set('flowgraph','asicflow','cts','0','input',('place','0'))"],
              schelp="""A list of inputs for the current step and index, specified as a
              (step,index) tuple.""")
  
      # flowgraph metric weights
      metric='default'
      scparam(cfg,['flowgraph', flow, step, index, 'weight', metric],
              sctype='float',
              shorthelp="Flowgraph: metric weights",
              switch="-flowgraph_weight 'flow step index metric <float>'",
              example=[
                  "cli: -flowgraph_weight 'asicflow cts 0 area_cells 1.0'",
                  "api:  chip.set('flowgraph','asicflow','cts','0','weight','area_cells',1.0)"],
              schelp="""Weights specified on a per step and per metric basis used to give
              effective "goodnes" score for a step by calculating the sum all step
              real metrics results by the corresponding per step weights.""")
  
      scparam(cfg,['flowgraph', flow, step, index, 'goal', metric],
              sctype='float',
              shorthelp="Flowgraph: metric goals",
              switch="-flowgraph_goal 'flow step index metric <float>'",
              example=[
                  "cli: -flowgraph_goal 'asicflow cts 0 area_cells 1.0'",
                  "api:  chip.set('flowgraph','asicflow','cts','0','goal','errors', 0)"],
              schelp="""Goals specified on a per step and per metric basis used to
              determine whether a certain task can be considered when merging
              multiple tasks at a minimum or maximum node. A task is considered
              failing if the absolute value of any of its metrics are larger than
              the goal for that metric, if set.""")
  
      # flowgraph tool
      scparam(cfg,['flowgraph', flow, step, index, 'tool'],
              sctype='str',
              shorthelp="Flowgraph: tool selection",
              switch="-flowgraph_tool 'flow step <str>'",
              example=[
                  "cli: -flowgraph_tool 'asicflow place 0 openroad'",
                  "api: chip.set('flowgraph','asicflow','place','0','tool','openroad')"],
              schelp="""Name of the tool name used for task execution. Builtin tool names
              associated bound to core API functions include: minimum, maximum, join,
              verify, mux.""")
  
      # flowgraph arguments
      scparam(cfg,['flowgraph', flow, step, index, 'args'],
              sctype='[str]',
              shorthelp="Flowgraph: setup arguments",
              switch="-flowgraph_args 'flow step index <str>'",
              example=[
                  "cli: -flowgraph_args 'asicflow cts 0 0'",
                  "api:  chip.add('flowgraph','asicflow','cts','0','args','0')"],
              schelp="""User specified flowgraph string arguments specified on a per
              step and per index basis.""")
  
      # flowgraph valid bits
      scparam(cfg,['flowgraph', flow, step, index, 'valid'],
              sctype='bool',
              shorthelp="Flowgraph: task valid bit",
              switch="-flowgraph_valid 'flow step index <str>'",
              example=[
                  "cli: -flowgraph_valid 'asicflow cts 0 true'",
                  "api:  chip.set('flowgraph','asicflow','cts','0','valid',True)"],
              schelp="""Flowgraph valid bit specified on a per step and per index basis.
              The parameter can be used to control flow execution. If the bit
              is cleared (0), then the step/index combination is invalid and
              should not be run.""")
  
      # flowgraph timeout value
      scparam(cfg,['flowgraph', flow, step, index, 'timeout'],
              sctype='float',
              unit='s',
              shorthelp="Flowgraph: task timeout value",
              switch="-flowgraph_timeout 'flow step 0 <float>'",
              example=[
                  "cli: -flowgraph_timeout 'asicflow cts 0 3600'",
                  "api:  chip.set('flowgraph','asicflow','cts','0','timeout', 3600)"],
              schelp="""Timeout value in seconds specified on a per step and per index
              basis. The flowgraph timeout value is compared against the
              wall time tracked by the SC runtime to determine if an
              operation should continue. Timeout values help in situations
              where 1.) an operation is stuck and may never finish. 2.) the
              operation progress has saturated and continued execution has
              a negative return on investment.""")
  
      # flowgraph status
      scparam(cfg,['flowgraph', flow, step, index, 'status'],
              sctype='str',
              shorthelp="Flowgraph: task status",
              switch="-flowgraph_status 'flow step index <str>'",
              example=[
                  "cli: -flowgraph_status 'asicflow cts 10 success'",
                  "api:  chip.set('flowgraph','asicflow', 'cts','10','status', 'success')"],
              schelp="""Parameter that tracks the status of a task. Valid values are:
  
              * "success": task ran successfully
              * "error": task failed with an error
  
              An empty value indicates the task has not yet been completed.""")
  
      # flowgraph select
      scparam(cfg,['flowgraph', flow, step, index, 'select'],
              sctype='[(str,str)]',
              shorthelp="Flowgraph: task select record",
              switch="-flowgraph_select 'flow step index <(str,str)>'",
              example= [
                  "cli: -flowgraph_select 'asicflow cts 0 (place,42)'",
                  "api:  chip.set('flowgraph','asicflow', 'cts','0','select',('place','42'))"],
              schelp="""
              List of selected inputs for the current step/index specified as
              (in_step,in_index) tuple.""")
  
      return cfg
  
  
  ###########################################################################
  # Tool Setup
  ###########################################################################
  
  def schema_tool(cfg, tool='default', step='default', index='default'):
  
      key = 'default'
      suffix = 'default'
      metric = 'default'
  
      scparam(cfg, ['tool', tool, 'exe'],
              sctype='str',
              shorthelp="Tool: executable name",
              switch="-tool_exe 'tool <str>'",
              example=["cli: -tool_exe 'openroad openroad'",
                       "api:  chip.set('tool','openroad','exe','openroad')"],
              schelp="""Tool executable name.""")
  
      scparam(cfg, ['tool', tool, 'path'],
              sctype='dir',
              shorthelp="Tool: executable path",
              switch="-tool_path 'tool <dir>'",
              example=["cli: -tool_path 'openroad /usr/local/bin'",
                       "api:  chip.set('tool','openroad','path','/usr/local/bin')"],
              schelp="""
              File system path to tool executable. The path is prepended to the
              system PATH environment variable for batch and interactive runs. The
              path parameter can be left blank if the 'exe' is already in the
              environment search path.""")
  
      scparam(cfg, ['tool', tool, 'vswitch'],
              sctype='[str]',
              shorthelp="Tool: executable version switch",
              switch="-tool_vswitch 'tool <str>'",
              example=["cli: -tool_vswitch 'openroad -version'",
                       "api:  chip.set('tool','openroad','vswitch','-version')"],
              schelp="""
              Command line switch to use with executable used to print out
              the version number. Common switches include -v, -version,
              --version. Some tools may require extra flags to run in batch mode.""")
  
      scparam(cfg, ['tool', tool, 'vendor'],
              sctype='str',
              shorthelp="Tool: vendor",
              switch="-tool_vendor 'tool <str>'",
              example=["cli: -tool_vendor 'yosys yosys'",
                       "api: chip.set('tool','yosys','vendor','yosys')"],
              schelp="""
              Name of the tool vendor. Parameter can be used to set vendor
              specific technology variables in the PDK and libraries. For
              open source projects, the project name should be used in
              place of vendor.""")
  
      scparam(cfg, ['tool', tool, 'version'],
              sctype='[str]',
              shorthelp="Tool: version",
              switch="-tool_version 'tool <str>'",
              example=["cli: -tool_version 'openroad >=v2.0'",
                       "api:  chip.set('tool','openroad','version','>=v2.0')"],
              schelp="""
              List of acceptable versions of the tool executable to be used. Each
              entry in this list must be a version specifier as described by Python
              `PEP-440 <https://peps.python.org/pep-0440/#version-specifiers>`_.
              During task execution, the tool is called with the 'vswitch' to
              check the runtime executable version. If the version of the system
              executable is not allowed by any of the specifiers in 'version',
              then the job is halted pre-execution. For backwards compatibility,
              entries that do not conform to the standard will be interpreted as a
              version with an '==' specifier. This check can be disabled by
              setting 'novercheck' to True.""")
  
      scparam(cfg, ['tool', tool, 'format'],
              sctype='str',
              shorthelp="Tool: manifest file format",
              switch="-tool_format 'tool <file>'",
              example=[ "cli: -tool_format 'yosys tcl'",
                        "api: chip.set('tool','yosys','format','tcl')"],
              schelp="""
              File format for tool manifest handoff. Supported formats are tcl,
              yaml, and json.""")
  
      scparam(cfg, ['tool', tool, 'warningoff'],
              sctype='[str]',
              shorthelp="Tool: warning filter",
              switch="-tool_warningoff 'tool <str>'",
              example=["cli: -tool_warningoff 'verilator COMBDLY'",
                       "api: chip.set('tool','verilator','warningoff','COMBDLY')"],
              schelp="""
              A list of EDA warnings for which printing should be suppressed.
              Generally this is done on a per design basis after review has
              determined that warning can be safely ignored The code for turning
              off warnings can be found in the specific tool reference manual.
              """)
  
      scparam(cfg, ['tool', tool, 'continue'],
              sctype='bool',
              shorthelp="Tool: continue-on-error option",
              switch="-tool_continue 'tool <bool>'",
              example=["cli: -tool_continue 'verilator true'",
                       "api: chip.set('tool','verilator','continue', true)"],
              schelp="""
              Directs tool to continue operating even if errors are
              encountered.""")
  
      scparam(cfg, ['tool', tool, 'licenseserver', key],
              sctype='[str]',
              shorthelp="Tool: license servers",
              switch="-tool_licenseserver 'name key <str>'",
              example=[
                  "cli: -tool_licenseserver 'atool ACME_LICENSE 1700@server'",
                  "api: chip.set('tool','atool','licenseserver','ACME_LICENSE','1700@server')"],
              schelp="""
              Defines a set of tool specific environment variables used by the executables
              that depend on license key servers to control access. For multiple servers,
              separate each server by a 'colon'. The named license variable are read at
              runtime (run()) and the environment variables are set.
              """)
  
      #######################
      # Per step/index
      ########################
  
      scparam(cfg, ['tool', tool, 'regex', step, index, suffix],
              sctype='[str]',
              shorthelp="Tool: regex filter",
              switch="-tool_regex 'tool step index suffix <str>'",
              example=[
                  "cli: -tool_regex 'openroad place 0 errors -v ERROR'",
                  "api: chip.set('tool','openroad','regex','place','0','errors','-v ERROR')"],
              schelp="""
              A list of piped together grep commands. Each entry represents a set
              of command line arguments for grep including the regex pattern to
              match. Starting with the first list entry, each grep output is piped
              into the following grep command in the list. Supported grep options
              include ``-v`` and ``-e``. Patterns starting with "-" should be
              directly preceeded by the ``-e`` option. The following example
              illustrates the concept.
  
              UNIX grep:
  
              .. code-block:: bash
  
                  $ grep WARNING place.log | grep -v "bbox" > place.warnings
  
              SiliconCompiler::
  
                  chip.set('tool', 'openroad', 'regex', 'place', '0', 'warnings', ["WARNING", "-v bbox"])
  
              The "errors" and "warnings" suffixes are special cases. When set,
              the number of matches found for these regexes will be used to set
              the errors and warnings metrics for the task, respectively.
              """)
  
  
      scparam(cfg, ['tool', tool, 'option', step, index],
              sctype='[str]',
              shorthelp="Tool: executable options",
              switch="-tool_option 'tool step index <str>'",
              example=[
                  "cli: -tool_option 'openroad cts 0 -no_init'",
                  "api: chip.set('tool','openroad','option','cts','0','-no_init')"],
              schelp="""
              List of command line options for the tool executable, specified on
              a per tool and per step basis. Options must not include spaces.
              For multiple argument options, each option is a separate list element.
              """)
  
      scparam(cfg, ['tool', tool, 'var', step, index, key],
              sctype='[str]',
              shorthelp="Tool: script variables",
              switch="-tool_variable 'tool step index key <str>'",
              example=[
                  "cli: -tool_variable 'openroad cts 0 myvar 42'",
                  "api: chip.set('tool','openroad','var','cts','0','myvar','42')"],
              schelp="""
              Tool script variables specified as key value pairs. Variable
              names and value types must match the name and type of tool and reference
              script consuming the variable.""")
  
      scparam(cfg, ['tool', tool, 'env', step, index, key],
              sctype='str',
              shorthelp="Tool: environment variables",
              switch="-tool_env 'tool step index name <str>'",
              example=[
                  "cli: -tool_env 'openroad cts 0 MYVAR 42'",
                  "api: chip.set('tool','openroad','env','cts','0','MYVAR','42')"],
              schelp="""
              Environment variables to set for individual tasks. Keys and values
              should be set in accordance with the tool's documentation. Most
              tools do not require extra environment variables to function.""")
  
      scparam(cfg, ['tool', tool, 'input', step, index],
              sctype='[file]',
              shorthelp="Tool: input files",
              switch="-tool_input 'tool step index <str>'",
              example=[
                  "cli: -tool_input 'openroad place 0 oh_add.def'",
                  "api: chip.set('tool','openroad','input','place','0','oh_add.def')"],
              schelp="""
              List of data files to be copied from previous flowgraph steps 'output'
              directory. The list of steps to copy files from is defined by the
              list defined by the dictionary key ['flowgraph', step, index, 'input'].
              All files must be available for flow to continue. If a file
              is missing, the program exists on an error.""")
  
      scparam(cfg, ['tool', tool, 'output', step, index],
              sctype='[file]',
              shorthelp="Tool: output files",
              switch="-tool_output 'tool step index <str>'",
              example=[
                  "cli: -tool_output 'openroad place 0 oh_add.def'",
                  "api: chip.set('tool','openroad','output','place','0','oh_add.def')"],
              schelp="""
              List of data files to be copied from previous flowgraph steps 'output'
              directory. The list of steps to copy files from is defined by the
              list defined by the dictionary key ['flowgraph', step, index, 'output'].
              All files must be available for flow to continue. If a file
              is missing, the program exists on an error.""")
  
      scparam(cfg, ['tool', tool, 'stdout', step, index, 'destination'],
              sctype='str',
              defvalue='log',
              scope='job',
              shorthelp="Tool: Destination for stdout",
              switch="-tool_stdout_destination 'tool step index [log|output|none]'",
              example=["cli: -tool_stdout_destination 'ghdl import 0 log'",
                      "api: chip.set('tool','ghdl','stdout','import','0','destination','log')"],
              schelp="""
              Defines where to direct the output generated over stdout.
              Supported options are:
              none: the stream generated to STDOUT is ignored
              log: the generated stream is stored in <step>.<suffix>; if not in quiet mode,
              it is additionally dumped to the display output: the generated stream is stored
              in outputs/<design>.<suffix>""")
  
      scparam(cfg, ['tool', tool, 'stdout', step, index, 'suffix'],
              sctype='str',
              defvalue='log',
              scope='job',
              shorthelp="Tool: File suffix for redirected stdout",
              switch="-tool_stdout_suffix 'tool step index <str>'",
              example=["cli: -tool_stdout_suffix 'ghdl import 0 log'",
                      "api: chip.set('tool','ghdl','stdout','import','0','suffix','log')"],
              schelp="""
              Specifies the file extension for the content redirected from stdout.""")
  
      scparam(cfg, ['tool', tool, 'stderr', step, index, 'destination'],
              sctype='str',
              defvalue='log',
              scope='job',
              shorthelp="Tool: Destination for stderr",
              switch="-tool_stderr_destination 'tool step index [log|output|none]'",
              example=["cli: -tool_stderr_destination 'ghdl import 0 log'",
                      "api: chip.set('tool','ghdl','stderr','import','0','destination','log')"],
              schelp="""
              Defines where to direct the output generated over stderr.
              Supported options are:
              none: the stream generated to STDERR is ignored
              log: the generated stream is stored in <step>.<suffix>; if not in quiet mode,
              it is additionally dumped to the display output: the generated stream is
              stored in outputs/<design>.<suffix>""")
  
      scparam(cfg, ['tool', tool, 'stderr', step, index, 'suffix'],
              sctype='str',
              defvalue='log',
              scope='job',
              shorthelp="Tool: File suffix for redirected stderr",
              switch="-tool_stderr_suffix 'tool step index <str>'",
              example=["cli: -tool_stderr_suffix 'ghdl import 0 log'",
                      "api: chip.set('tool','ghdl','stderr','import','0','suffix','log')"],
              schelp="""
              Specifies the file extension for the content redirected from stderr.""")
  
      scparam(cfg, ['tool', tool, 'require', step, index],
              sctype='[str]',
              shorthelp="Tool: parameter requirements",
              switch="-tool_require 'tool step index <str>'",
              example=[
                  "cli: -tool_require 'openroad cts 0 design'",
                  "api: chip.set('tool','openroad','require','cts','0','design')"],
              schelp="""
              List of keypaths to required tool parameters. The list is used
              by check() to verify that all parameters have been set up before
              step execution begins.""")
  
      scparam(cfg, ['tool', tool, 'report', step, index, metric],
              sctype='[file]',
              shorthelp="Tool: report files",
              switch="-tool_report 'tool step index metric <str>'",
              example=[
                   "cli: -tool_report 'openroad place 0 holdtns place.log'",
                  "api: chip.set('tool','openroad','report','syn','0','holdtns','place.log')"],
              schelp="""
              List of report files associated with a specific 'metric'. The file path
              specified is relative to the run directory of the current task.""")
  
      scparam(cfg, ['tool', tool, 'refdir', step, index],
              sctype='[dir]',
              shorthelp="Tool: script directory",
              switch="-tool_refdir 'tool step index <dir>'",
              example=[
                  "cli: -tool_refdir 'yosys syn 0 ./myref'",
                  "api:  chip.set('tool','yosys','refdir','syn','0','./myref')"],
              schelp="""
              Path to directories containing reference flow scripts, specified
              on a per step and index basis.""")
  
      scparam(cfg, ['tool', tool, 'script', step, index],
              sctype='[file]',
              shorthelp="Tool: entry script",
              switch="-tool_script 'tool step index <file>'",
              example=[
                  "cli: -tool_script 'yosys syn 0 syn.tcl'",
                  "api: chip.set('tool','yosys','script','syn','0','syn.tcl')"],
              schelp="""
              Path to the entry script called by the executable specified
              on a per tool and per step basis.""")
  
      scparam(cfg, ['tool', tool, 'keep', step, index],
              sctype='[str]',
              shorthelp="Tool: files to keep",
              switch="-tool_keep 'tool step index <str>'",
              example=[
                  "cli: -tool_keep 'surelog import 0 slp_all'",
                  "api: chip.set('tool','surelog','script','import','0','slpp_all')"],
              schelp="""
              Names of additional files and directories in the work directory that
              should be kept when :keypath:`option, clean` is true.""")
  
      scparam(cfg, ['tool', tool, 'threads', step, index],
              sctype='int',
              shorthelp="Tool: thread parallelism",
              switch="-tool_threads 'tool step index <int>'",
              example=["cli: -tool_threads 'magic drc 0 64'",
                       "api: chip.set('tool','magic','threads','drc','0','64')"],
              schelp="""
              Thread parallelism to use for execution specified on a per tool and per
              step basis. If not specified, SC queries the operating system and sets
              the threads based on the maximum thread count supported by the
              hardware.""")
  
      return cfg
  
  ###########################################################################
  #  Function arguments
  ###########################################################################
  
  def schema_arg(cfg):
  
      key = 'default'
      scparam(cfg, ['arg', 'pdk', key],
              sctype='[str]',
              scope='scratch',
              shorthelp="ARG: PDK argument",
              switch="-arg_pdk 'key <str>",
              example=[
                  "cli: -arg_pdk 'mimcap true'",
                  "api: chip.set('arg','pdk','mimcap','true')"],
              schelp="""
              Parameter passed in as key/value pair to the technology target
              referenced in the load_pdk() API call. See the target technology
              for specific guidelines regarding configuration parameters.""")
  
      scparam(cfg, ['arg', 'flow', key],
              sctype='[str]',
              scope='scratch',
              shorthelp="ARG: Flow argument",
              switch="-arg_flow 'key <str>'",
              example=[
                  "cli: -arg_flow 'n 100'",
                  "api: chip.set('arg','flow','n', 100)"],
              schelp="""
              Parameter passed in as key/value pair to the flow target
              referenced in the load_flow() API call. See the target flow
              for specific guidelines regarding configuration parameters.""")
  
      scparam(cfg, ['arg', 'step'],
              sctype='str',
              scope='scratch',
              shorthelp="ARG: Step argument",
              switch="-arg_step <str>",
              example=["cli: -arg_step 'route'",
                      "api: chip.set('arg', 'step', 'route')"],
              schelp="""
              Dynamic parameter passed in by the sc runtime as an argument to
              a runtime task. The parameter enables configuration code
              (usually TCL) to use control flow that depend on the current
              'step'. The parameter is used the run() function and
              is not intended for external use.""")
  
      scparam(cfg, ['arg', 'index'],
              sctype='str',
              scope='scratch',
              shorthelp="ARG: Index argument",
              switch="-arg_index <str>",
              example=["cli: -arg_index 0",
                      "api: chip.set('arg','index','0')"],
              schelp="""
              Dynamic parameter passed in by the sc runtime as an argument to
              a runtime task. The parameter enables configuration code
              (usually TCL) to use control flow that depend on the current
              'index'. The parameter is used the run() function and
              is not intended for external use.""")
  
      return cfg
  
  ###########################################################################
  # Metrics to Track
  ###########################################################################
  
  def schema_metric(cfg, step='default', index='default'):
  
      metrics = {'errors': 'errors',
                 'warnings' :'warnings',
                 'drvs' : 'design rule violations',
                 'unconstrained' : 'unconstrained timing paths'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='int',
                  shorthelp=f"Metric: total {item}",
                  switch=f"-metric_{item} 'step index <int>'",
                  example=[
                      f"cli: -metric_{item} 'dfm 0 0'",
                      f"api: chip.set('metric','dfm','0','{item}', 0)"],
                  schelp=f"""Metric tracking the total number of {val} on a
                  per step and index basis.""")
  
      scparam(cfg, ['metric', step, index, 'coverage'],
              sctype='float',
              unit='%',
              shorthelp=f"Metric: coverage",
              switch="-metric_coverage 'step index <float>'",
              example=[
                  "cli: -metric_coverage 'place 0 99.9'",
                  "api: chip.set('metric','place','0','coverage', 99.9)"],
              schelp=f"""
              Metric tracking the test coverage in the design expressed as a percentage
              with 100 meaning full coverage. The meaning of the metric depends on the
              task being executed. It can refer to code coverage, feature coverage,
              stuck at fault coverage.""")
  
      scparam(cfg, ['metric', step, index, 'security'],
              sctype='float',
              unit='%',
              shorthelp="Metric: security",
              switch="-metric_security 'step index <float>'",
              example=[
                  "cli: -metric_security 'place 0 100'",
                  "api: chip.set('metric','place','0','security', 100)"],
              schelp=f"""
              Metric tracking the level of security (1/vulnerability) of the design.
              A completely secure design would have a score of 100. There is no
              absolute scale for the security metrics (like with power, area, etc)
              so the metric will be task and tool dependent.""")
  
      metrics = {'luts': 'FPGA LUTs',
                 'dsps' :'FPGA DSP slices',
                 'brams' : 'FPGA BRAM tiles'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='int',
  
                  shorthelp=f"Metric: {val}",
                  switch=f"-metric_{item} 'step index <int>'",
                  example=[
                      f"cli: -metric_{item} 'place 0 100'",
                      f"api: chip.set('metric','place','0','{item}', 100)"],
                  schelp=f"""
                  Metric tracking the total {val} used by the design as reported
                  by the implementation tool. There is no standardized definition
                  for this metric across vendors, so metric comparisons can
                  generally only be done between runs on identical tools and
                  device families.""")
  
      metrics = {'cellarea': 'cell area (ignoring fillers)',
                 'totalarea' :'physical die area'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='float',
                  unit='um^2',
                  shorthelp=f"Metric: {item}",
                  switch=f"-metric_{item} 'step index <float>'",
                  example=[
                      f"cli: -metric_{item} 'place 0 100.00'",
                      f"api: chip.set('metric','place','0','{item}', 100.00)"],
                  schelp=f"""
                  Metric tracking the total {val} occupied by the design.""")
  
      scparam(cfg, ['metric', step, index, 'utilization'],
              sctype='float',
              unit='%',
              shorthelp=f"Metric: area utilization",
              switch=f"-metric_utilization step index <float>",
              example=[
                  f"cli: -metric_utilization 'place 0 50.00'",
                  f"api: chip.set('metric','place','0','utilization', 50.00)"],
              schelp=f"""
              Metric tracking the area utilization of the design calculated as
              100 * (cellarea/totalarea).""")
  
      metrics = {'peakpower': 'worst case total peak power',
                 'averagepower': 'average workload power',
                 'dozepower': 'power consumed while in low frequency operating mode',
                 'idlepower': 'power while not performing useful work',
                 'leakagepower' :'leakage power with rails active but without any dynamic switching activity',
                 'sleeppower': 'power consumed with some or all power rails gated off'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='float',
                  unit='mw',
                  shorthelp=f"Metric: {item}",
                  switch=f"-metric_{item} 'step index <float>'",
                  example=[
                      f"cli: -metric_{item} 'place 0 0.01'",
                      f"api: chip.set('metric','place','0','{item}', 0.01)"],
                  schelp=f"""
                  Metric tracking the {val} of the design specified on a per step
                  and index basis. Power metric depend heavily on the method
                  being used for extraction: dynamic vs static, workload
                  specification (vcd vs saif), power models, process/voltage/temperature.
                  The power {item} metric tries to capture the data that would
                  usually be reflected inside a datasheet given the approprate
                  footnote conditions.""")
  
      scparam(cfg, ['metric', step, index, 'irdrop'],
              sctype='float',
              unit='mv',
              shorthelp=f"Metric: peak IR drop",
              switch="-metric_irdrop 'step index <float>'",
              example=[
                  f"cli: -metric_irdrop 'place 0 0.05'",
                  f"api: chip.set('metric','place','0','irdrop', 0.05)"],
              schelp=f"""
              Metric tracking the peak IR drop in the design based on extracted
              power and ground rail parasitics, library power models, and
              switching activity. The switching activity calculated on a per
              node basis is taken from one of three possible sources, in order
              of priority: VCD file, SAIF file, 'activityfactor' parameter.""")
  
      metrics = {'holdpaths': 'hold',
                 'setuppaths': 'setup'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='int',
                  shorthelp=f"Metric: {item}",
                  switch=f"-metric_{item} 'step index <float>'",
                  example=[
                      f"cli: -metric_{item} 'place 0 10'",
                      f"api: chip.set('metric','place','0','{item}', 10)"],
                  schelp=f"""
                  Metric tracking the total number of timing paths violating {val}
                  constraints.""")
  
      metrics = {'holdslack': 'worst hold slack (positive or negative)',
                 'holdwns': 'worst negative hold slack (positive values truncated to zero)',
                 'holdtns': 'total negative hold slack (TNS)',
                 'setupslack': 'worst setup slack (positive or negative)',
                 'setupwns': 'worst negative setup slack (positive values truncated to zero)',
                 'setuptns': 'total negative setup slack (TNS)'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='float',
                  unit='ns',
                  shorthelp=f"Metric: {item}",
                  switch=f"-metric_{item} 'step index <float>'",
                  example=[
                      f"cli: -metric_{item} 'place 0 0.01'",
                      f"api: chip.set('metric','place','0','{item}', 0.01)"],
                  schelp=f"""
                  Metric tracking the {val} on a per step and index basis.""")
  
      metrics = {'macros': 'macros',
                 'cells': 'cell instances',
                 'registers': 'register instances',
                 'buffers': 'buffer and inverter instances',
                 'transistors': 'transistors',
                 'pins': 'pins',
                 'nets': 'nets',
                 'vias': 'vias'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='int',
                  shorthelp=f"Metric: {item}",
                  switch=f"-metric_{item} 'step index <float>'",
                  example=[
                      f"cli: -metric_{item} 'place 0 100'",
                      f"api: chip.set('metric','place','0','{item}', 50)"],
                  schelp=f"""
                  Metric tracking the total number of {val} in the design
                  on a per step and index basis.""")
  
      item = 'wirelength'
      scparam(cfg, ['metric', step, index, item],
              sctype='float',
              unit='um',
              shorthelp=f"Metric: {item}",
              switch=f"-metric_{item} 'step index <float>'",
              example=[
                  f"cli: -metric_{item} 'place 0 100.0'",
                  f"api: chip.set('metric','place','0','{item}', 50.0)"],
              schelp=f"""
              Metric tracking the total {item} of the design on a per step
              and index basis.""")
  
      item = 'overflow'
      scparam(cfg, ['metric', step, index, item],
              sctype='int',
              shorthelp=f"Metric: {item}",
              switch=f"-metric_{item} 'step index <float>'",
              example=[
                  f"cli: -metric_{item} 'place 0 0'",
                  f"api: chip.set('metric','place','0','{item}', 50)"],
              schelp=f"""
              Metric tracking the total number of overflow tracks for the routing
              on per step and index basis. Any non-zero number suggests an over
              congested design. To analyze where the congestion is occurring
              inspect the router log files for detailed per metal overflow
              reporting and open up the design to find routing hotspots.""")
  
      item = 'memory'
      scparam(cfg, ['metric', step, index, item],
              sctype='float',
              unit='B',
              scope='job',
              shorthelp=f"Metric: {item}",
              switch=f"-metric_{item} 'step index <float>'",
              example=[
                  f"cli: -metric_{item} 'dfm 0 10e9'",
                  f"api: chip.set('metric','dfm','0','{item}', 10e9)"],
              schelp=f"""
              Metric tracking total peak program memory footprint on a per
              step and index basis.""")
  
      item = 'exetime'
      scparam(cfg, ['metric', step, index, item],
              sctype='float',
              unit='s',
              shorthelp=f"Metric: {item}",
              switch=f"-metric_{item} 'step index <float>'",
              example=[
                  f"cli: -metric_{item} 'dfm 0 10.0'",
                  f"api: chip.set('metric','dfm','0','{item}', 10.0)"],
              schelp=f"""
              Metric tracking time spent by the eda executable 'exe' on a
              per step and index basis. It does not include the siliconcompiler
              runtime overhead or time waitig for I/O operations and
              inter-processor communication to complete.""")
  
      item = 'tasktime'
      scparam(cfg, ['metric', step, index, item],
              sctype='float',
              unit='s',
              shorthelp=f"Metric: {item}",
              switch=f"-metric_{item} 'step index <float>'",
              example=[
                  f"cli: -metric_{item} 'dfm 0 10.0'",
                  f"api: chip.set('metric','dfm','0','{item}', 10.0)"],
              schelp=f"""
              Metric trakcing the total amount of time spent on a task from
              beginning to end, including data transfers and pre/post
              processing.""")
  
      item = 'totaltime'
      scparam(cfg, ['metric', step, index, item],
              sctype='float',
              unit='s',
              shorthelp=f"Metric: {item}",
              switch=f"-metric_{item} 'step index <float>'",
              example=[
                  f"cli: -metric_{item} 'dfm 0 10.0'",
                  f"api: chip.set('metric','dfm','0','{item}', 10.0)"],
              schelp=f"""
              Metric tracking the total amount of time spent from the beginning
              of the run up to and including the current step and index.""")
  
      return cfg
  
  ###########################################################################
  # Design Tracking
  ###########################################################################
  
  def schema_record(cfg, step='default', index='default'):
  
      # setting up local data structure
      # <key>  : ['short help', 'example' 'extended help']
  
      records = {'userid': ['userid',
                            'wiley',
                            ''],
                 'publickey' : ['public key',
                                '<key>',
                                ''],
                 'machine' : ['machine name',
                              'carbon',
                              '(myhost, localhost, ...'],
                 'macaddr' : ['MAC address',
                              '<addr>',
                              ''],
                 'ipaddr' : ['IP address',
                             '<addr>',
                             ''],
                 'platform' : ['platform name',
                               'linux',
                               '(linux, windows, freebsd)'],
                 'distro' : ['distro name',
                             'ubuntu',
                             '(ubuntu, redhat, centos)'],
                 'arch' : ['hardware architecture',
                           'x86_64',
                           '(x86_64, rv64imafdc)'],
                 'starttime' : ['start time',
                                '2021-09-06 12:20:20',
                                'Time is reported in the ISO 8601 format YYYY-MM-DD HR:MIN:SEC'],
                 'endtime' : ['end time',
                              '2021-09-06 12:20:20',
                              'Time is reported in the ISO 8601 format YYYY-MM-DD HR:MIN:SEC'],
                 'region' : ['cloud region',
                             'US Gov Boston',
                             """Recommended naming methodology:
  
                             * local: node is the local machine
                             * onprem: node in on-premises IT infrastructure
                             * public: generic public cloud
                             * govcloud: generic US government cloud
                             * <region>: cloud and entity specific region string name
                             """],
                 'scversion': ['software version',
                               '1.0',
                               """Version number for the SiliconCompiler software."""],
                 'toolversion': ['tool version',
                                 '1.0',
                                 """The tool version captured correspnds to the 'tool'
                                 parameter within the 'eda' dictionary."""],
                 'toolpath': ['tool path',
                               '/usr/bin/openroad',
                               """Full path to tool executable used to run this
                               task."""],
                 'toolargs': ['tool CLI arguments',
                              '-I include/ foo.v',
                              'Arguments passed to tool via CLI.'],
                 'osversion': ['O/S version',
                               '20.04.1-Ubuntu',
                               """Since there is not standard version system for operating
                               systems, extracting information from is platform dependent.
                               For Linux based operating systems, the 'osversion' is the
                               version of the distro."""],
                 'kernelversion' : ['O/S kernel version',
                                    '5.11.0-34-generic',
                                    """Used for platforms that support a distinction
                                    between os kernels and os distributions."""]
      }
  
      for item,val in records.items():
          helpext = utils.trim(val[2])
          scparam(cfg, ['record', step, index, item],
                  sctype='str',
                  shorthelp=f"Record: {val[0]}",
                  switch=f"-record_{item} 'step index <str>'",
                  example=[
                      f"cli: -record_{item} 'dfm 0 <{val[1]}>'",
                      f"api: chip.set('record','dfm','0','{item}', <{val[1]}>)"],
                  schelp=f'Record tracking the {val[0]} per step and index basis. {helpext}')
  
      return cfg
  
  ###########################################################################
  # Global units
  ###########################################################################
  
  def schema_unit(cfg):
      '''
  
      '''
  
      units = {
          'time' : 'ns',
          'length' : 'um',
          'mass' : 'g',
          'capacitance' : 'pf',
          'resistance' : 'ohm',
          'inductance' : 'nh',
          'voltage' : 'mv',
          'current' : 'ma',
          'power' : 'mw',
          'energy' : 'pj'
      }
  
      for item,val in units.items():
          scparam(cfg, ['unit', item],
                  sctype='str',
                  defvalue=val,
                  shorthelp=f"Unit: {item}",
                  switch=f"-unit_{item} '<str>'",
                  example=[
                      f"cli: -unit_{item} '{val}'",
                      f"api: chip.set('unit','{item}',{val})"],
                  schelp=f"""
                  Units used for {item} when not explicitly specified.""")
  
      return cfg
  
  ###########################################################################
  # Run Options
  ###########################################################################
  
  def schema_option(cfg):
      ''' Technology agnostic run time options
      '''
  
      scparam(cfg, ['option', 'remote'],
              sctype='bool',
              scope='job',
              shorthelp="Enable remote processing",
              switch="-remote <bool>",
              example=[
                  "cli: -remote",
                  "api: chip.set('option','remote', True)"],
              schelp="""
              Sends job for remote processing if set to true. The remote
              option requires a credentials file to be placed in the home
              directory. Fore more information, see the credentials
              parameter.""")
  
      scparam(cfg, ['option', 'credentials'],
              sctype='[file]',
              scope='job',
              shorthelp="User credentials file",
              switch="-credentials <file>'",
              example=[
                  "cli: -credentials /home/user/.sc/credentials",
                  "api: chip.set('option', 'credentials','/home/user/.sc/credentials')"],
              schelp="""
              Filepath to credentials used for remote processing. If the
              credentials parameter is empty, the remote processing client program
              tries to access the ".sc/credentials" file in the user's home
              directory. The file supports the following fields:
  
              userid=<user id>
              secret_key=<secret key used for authentication>
              server=<ipaddr or url>""")
  
      scparam(cfg, ['option', 'jobscheduler'],
              sctype='str',
              scope='job',
              shorthelp="Job scheduler name",
              switch="-jobscheduler <str>",
              example=[
                  "cli: -jobscheduler slurm",
                  "api: chip.set('option','jobscheduler','slurm')"],
              schelp="""
              Sets the type of job scheduler to be used for each individual
              flowgraph steps. If the parameter is undefined, the steps are executed
              on the same machine that the SC was launched on. If 'slurm' is used,
              the host running the 'sc' command must be running a 'slurmctld' daemon
              managing a Slurm cluster. Additionally, the build directory ('-dir')
              must be located in shared storage which can be accessed by all hosts
              in the cluster.""")
  
      # Compilation
      scparam(cfg, ['option', 'mode'],
              sctype='str',
              scope='job',
              shorthelp="Compilation mode",
              switch="-mode <str>",
              example=[
              "cli: -mode asic",
              "api: chip.set('option','mode','asic')"],
              schelp="""
              Sets the operating mode of the compiler. Valid modes are:
              asic: RTL to GDS ASIC compilation
              fpga: RTL to bitstream FPGA compilation
              sim: simulation to verify design and compilation
              """)
  
      scparam(cfg, ['option','target'],
              sctype='str',
              scope='job',
              shorthelp="Compilation target",
              switch="-target <str>",
              example=["cli: -target freepdk45_demo",
                       "api: chip.set('option','target','freepdk45_demo')"],
              schelp="""
              Sets a target module to be used for compilation. The target
              module must set up all paramaters needed. The target module
              may load multiple flows and libraries.
              """)
  
      scparam(cfg, ['option','flow'],
              sctype='str',
              scope='job',
              shorthelp="Flow target",
              switch="-flow <str>",
              example=["cli: -flow asicfow",
                       "api: chip.set('option','flow','asicflow')"],
              schelp="""
              Sets the flow for the current run. The flow name
              must match up with a 'flow' in the flowgraph""")
  
      scparam(cfg, ['option','pdk'],
              sctype='str',
              scope='job',
              shorthelp="PDK target",
              switch="-pdk <str>",
              example=["cli: -pdk asap7",
                       "api: chip.set('option','pdk','asap7')"],
              schelp="""
              Sets the pdk for the current run. The pdk name
              must match up with a 'pdk' loaded with load_pdk.""")
  
      scparam(cfg, ['option','optmode'],
              sctype='str',
              scope='job',
              require='all',
              defvalue='O0',
              shorthelp="Optimization mode",
              switch="-O<str>",
              example=["cli: -O3",
                      "api: chip.set('option','optmode','O3')"],
              schelp="""
              The compiler has modes to prioritize run time and ppa. Modes
              include.
  
              (O0) = Exploration mode for debugging setup
              (O1) = Higher effort and better PPA than O0
              (O2) = Higher effort and better PPA than O1
              (O3) = Signoff quality. Better PPA and higher run times than O2
              (O4-O98) = Reserved (compiler/target dependent)
              (O99) = Experimental highest possible effort, may be unstable
              """)
  
      #TODO: with modular flows does this go away?
      scparam(cfg, ['option','frontend'],
              sctype='str',
              scope='job',
              defvalue='verilog',
              shorthelp="Compilation frontend",
              switch="-frontend <frontend>",
              example=["cli: -frontend systemverilog",
                       "api: chip.set('option','frontend', 'systemverilog')"],
              schelp="""
              Specifies the frontend that flows should use for importing and
              processing source files. Default option is 'verilog', also supports
              'systemverilog' and 'chisel'. When using the Python API, this parameter
              must be configured before calling load_target().""")
  
      scparam(cfg, ['option','cfg'],
              sctype='[file]',
              scope='job',
              shorthelp="Configuration manifest",
              switch="-cfg <file>",
              example=["cli: -cfg mypdk.json",
                      "api: chip.set('option','cfg','mypdk.json')"],
              schelp="""
              List of filepaths to JSON formatted schema configuration
              manifests. The files are read in automatically when using the
              'sc' command line application. In Python programs, JSON manifests
              can be merged into the current working manifest using the
              read_manifest() method.""")
  
      key = 'default'
      scparam(cfg, ['option', 'env', key],
              sctype='str',
              scope='job',
              shorthelp="Environment variables",
              switch="-env 'key <str>",
              example=[
              "cli: -env 'PDK_HOME /disk/mypdk'",
              "api: chip.set('option','env', 'PDK_HOME', '/disk/mypdk')"],
              schelp="""
              Certain tools and reference flows require global environment
              variables to be set. These variables can be managed externally or
              specified through the env variable.""")
  
      scparam(cfg, ['option', 'scpath'],
              sctype='[dir]',
              scope='job',
              shorthelp="Search path",
              switch="-scpath <dir>",
              example=[
                  "cli: -scpath '/home/$USER/sclib'",
                  "api: chip.set('option', 'scpath','/home/$USER/sclib')"],
              schelp="""
              Specifies python modules paths for target import.""")
  
      scparam(cfg, ['option', 'loglevel'],
              sctype='str',
              scope='job',
              defvalue='INFO',
              shorthelp="Logging level",
              switch="-loglevel <str>",
              example=[
                  "cli: -loglevel INFO",
                  "api: chip.set('option', 'loglevel', 'INFO')"],
              schelp="""
              Provides explicit control over the level of debug logging printed.
              Valid entries include INFO, DEBUG, WARNING, ERROR.""")
  
      scparam(cfg, ['option', 'builddir'],
              sctype='dir',
              scope='job',
              defvalue='build',
              shorthelp="Build directory",
              switch="-builddir <dir>",
              example=[
                  "cli: -builddir ./build_the_future",
                  "api: chip.set('option', 'builddir','./build_the_future')"],
              schelp="""
              The default build directory is in the local './build' where SC was
              executed. The 'builddir' parameter can be used to set an alternate
              compilation directory path.""")
  
      scparam(cfg, ['option', 'jobname'],
              sctype='str',
              scope='job',
              defvalue='job0',
              shorthelp="Job name",
              switch="-jobname <str>",
              example=[
                  "cli: -jobname may1",
                  "api: chip.set('option','jobname','may1')"],
              schelp="""
              Jobname during invocation of run(). The jobname combined with a
              defined director structure (<dir>/<design>/<jobname>/<step>/<index>)
              enables multiple levels of transparent job, step, and index
              introspection.""")
  
      #TODO: remove?
      scparam(cfg, ['option','jobinput','default','default'],
              sctype='str',
              scope='job',
              shorthelp="Input job name",
              switch="-jobinput 'step index <str>'",
              example=[
                  "cli: -jobinput 'cts 0 job0'",
                  "api:  chip.set('option','jobinput','cts,'0','job0')"],
              schelp="""
              Specifies jobname inputs for the current run() on a per step
              and per index basis. During execution, the default behavior is to
              copy inputs from the current job.""")
  
      scparam(cfg, ['option', 'steplist'],
              sctype='[str]',
              scope='job',
              shorthelp="Compilation step list",
              switch="-steplist <step>",
              example=[
                  "cli: -steplist 'import'",
                  "api: chip.set('option','steplist','import')"],
              schelp="""
              List of steps to execute. The default is to execute all steps
              defined in the flow graph.""")
  
      scparam(cfg, ['option', 'skipstep'],
              sctype='[str]',
              scope='job',
              shorthelp="Skip step list",
              switch="-skipstep <str>",
              example=[
                  "cli: -skipstep lvs",
                  "api: chip.set('option','skipstep','lvs')"],
              schelp="""
              List of steps to skip during execution.The default is to
              execute all steps  defined in the flow graph.""")
  
      scparam(cfg, ['option', 'indexlist'],
              sctype='[str]',
              scope='job',
              shorthelp="Compilation index list",
              switch="-indexlist <index>",
              example=["cli: -indexlist 0",
                      "api: chip.set('option','indexlist','0')"],
              schelp="""
              List of indices to execute. The default is to execute all
              indices for each step of a run.""")
  
      scparam(cfg, ['option', 'bkpt'],
              sctype='[str]',
              scope='job',
              shorthelp="Breakpoint list",
              switch="-bkpt <str>",
              example=[
                  "cli: -bkpt place",
                  "api: chip.set('option,'bkpt','place')"],
              schelp="""
              List of step stop (break) points. If the step is a TCL
              based tool, then the breakpoints stops the flow inside the
              EDA tool. If the step is a command line tool, then the flow
              drops into a Python interpreter.""")
  
      scparam(cfg, ['option', 'msgevent'],
              sctype='[str]',
              scope='job',
              shorthelp="Message event trigger",
              switch="-msgevent <str>",
              example=["cli: -msgevent export",
                      "api: chip.set('option','msgevent','export')"],
              schelp="""
              A list of steps after which to notify a recipient. For
              example if values of syn, place, cts are entered separate
              messages would be sent after the completion of the syn,
              place, and cts steps.""")
  
      scparam(cfg, ['option', 'msgcontact'],
              sctype='[str]',
              scope='job',
              shorthelp="Message contact",
              switch="-msgcontact <str>",
              example=[
                  "cli: -msgcontact 'wile.e.coyote@acme.com'",
                  "api: chip.set('option','msgcontact','wiley@acme.com')"],
              schelp="""
              A list of phone numbers or email addresses to message
              on a event event within the msg_event param. Actual
              support for email and phone messages is platform
              dependent.""")
  
      filetype = 'default'
      scparam(cfg, ['option', 'showtool', filetype],
              sctype='str',
              scope='job',
              shorthelp="Select data display tool",
              switch="-showtool 'filetype <tool>'",
              example=["cli: -showtool 'gds klayout'",
                      "api: chip.set('option','showtool','gds','klayout')"],
              schelp="""
              Selects the tool to use by the show function for displaying
              the specified filetype.""")
  
      scparam(cfg, ['option', 'metricoff'],
              sctype='[str]',
              scope='job',
              shorthelp="Metric summary filter",
              switch="-metricoff '<str>'",
              example=[
                  "cli: -metricoff 'wirelength'",
                  "api: chip.set('option','metricoff','wirelength')"],
              schelp="""
              List of metrics to supress when printing out the run
              summary.""")
  
      # Booleans
      scparam(cfg, ['option', 'clean'],
              sctype='bool',
              scope='job',
              shorthelp="Clean up after run",
              switch="-clean <bool>",
              example=["cli: -clean",
                       "api: chip.set('option','clean',True)"],
              schelp="""
              Clean up all intermediate and non essential files at the end
              of a task, leaving the following:
  
              * log file
              * replay.sh
              * inputs/
              * outputs/
              * reports/
              * autogenerated manifests
              * any files generated by schema-specified regexes
              * files specified by :keypath:`tool, <tool>, keep`""")
  
      scparam(cfg, ['option', 'hash'],
              sctype='bool',
              scope='job',
              shorthelp="Enable file hashing",
              switch="-hash <bool>",
              example=["cli: -hash",
                       "api: chip.set('option','hash',True)"],
              schelp="""
              Enables hashing of all inputs and outputs during
              compilation. The hash values are stored in the hashvalue
              field of the individual parameters.""")
  
      scparam(cfg, ['option', 'nodisplay'],
              sctype='bool',
              scope='job',
              shorthelp="Headless execution",
              switch="-nodisplay <bool>",
              example=["cli: -nodisplay",
                       "api: chip.set('option','nodisplay',True)"],
              schelp="""
              The '-nodisplay' flag prevents SiliconCompiler from
              opening GUI windows such as the final metrics report.""")
  
      scparam(cfg, ['option', 'quiet'],
              sctype='bool',
              scope='job',
              shorthelp="Quiet execution",
              switch="-quiet <bool>",
              example=["cli: -quiet",
                      "api: chip.set('option','quiet',True)"],
              schelp="""
              The -quiet option forces all steps to print to a log file.
              This can be useful with Modern EDA tools which print
              significant content to the screen.""")
  
      scparam(cfg, ['option', 'jobincr'],
              sctype='bool',
              scope='job',
              shorthelp="Autoincrement jobname",
              switch="-jobincr <bool>",
              example=["cli: -jobincr",
                      "api: chip.set('option','jobincr',True)"],
              schelp="""
              Forces an auto-update of the jobname parameter if a directory
              matching the jobname is found in the build directory. If the
              jobname does not include a trailing digit, then a the number
              '1' is added to the jobname before updating the jobname
              parameter.""")
  
      scparam(cfg, ['option', 'novercheck'],
              sctype='bool',
              defvalue='false',
              scope='job',
              shorthelp="Disable version checking",
              switch="-novercheck <bool>",
              example=["cli: -novercheck",
                      "api: chip.set('option','novercheck',True)"],
              schelp="""
              Disables strict version checking on all invoked tools if True.
              The list of supported version numbers is defined in the
              'version' parameter in the 'eda' dictionary for each tool.""")
  
      scparam(cfg, ['option', 'relax'],
              sctype='bool',
              scope='job',
              shorthelp="Relax RTL linting",
              switch="-relax <bool>",
              example=["cli: -relax",
                      "api: chip.set('option','relax',True)"],
              schelp="""
              Specifies that tools should be lenient and suppress some
              warnings that may or may not indicate design issues.""")
  
      scparam(cfg, ['option', 'resume'],
              sctype='bool',
              scope='job',
              shorthelp="Resume build",
              switch="-resume <bool>",
              example=["cli: -resume",
                      "api: chip.set('option','resume',True)"],
              schelp="""
              If results exist for current job, then don't re-run any steps that
              had at least one index run successfully. Useful for debugging a
              flow that failed partway through.
              """)
  
      scparam(cfg, ['option', 'track'],
              sctype='bool',
              scope='job',
              shorthelp="Enable provenance tracking",
              switch="-track <bool>",
              example=["cli: -track",
                      "api: chip.set('option','track',True)"],
              schelp="""
              Turns on tracking of all 'record' parameters during each
              task. Tracking will result in potentially sensitive data
              being recorded in the manifest so only turn on this feature
              if you have control of the final manifest.""")
  
      scparam(cfg, ['option', 'trace'],
              sctype='bool',
              scope='job',
              shorthelp="Enable debug traces",
              switch="-trace <bool>",
              example=["cli: -trace",
                      "api: chip.set('option','trace',True)"],
              schelp="""
              Enables debug tracing during compilation and/or runtime.""")
  
      scparam(cfg, ['option', 'skipall'],
              sctype='bool',
              scope='job',
              shorthelp="Skip all tasks",
              switch="-skipall <bool>",
              example=["cli: -skipall",
                      "api: chip.set('option','skipall',True)"],
              schelp="""
              Skips the execution of all tools in run(), enabling a quick
              check of tool and setup without having to run through each
              step of a flow to completion.""")
  
      scparam(cfg, ['option', 'skipcheck'],
              sctype='bool',
              scope='job',
              shorthelp="Skip manifest check",
              switch="-skipcheck <bool>",
              example=["cli: -skipcheck",
                       "api: chip.set('option','skipcheck',True)"],
              schelp="""
              Bypasses the strict runtime manifest check. Can be used for
              accelerating initial bringup of tool/flow/pdk/libs targets.
              The flag should not be used for production compilation.""")
  
      scparam(cfg, ['option', 'copyall'],
              sctype='bool',
              scope='job',
              shorthelp="Copy all inputs to build directory",
              switch="-copyall <bool>",
              example=["cli: -copyall",
                      "api: chip.set('option','copyall',True)"],
              schelp="""
              Specifies that all used files should be copied into the
              build directory, overriding the per schema entry copy
              settings.""")
  
      scparam(cfg, ['option', 'show'],
              sctype='bool',
              scope='job',
              shorthelp="Show layout",
              switch="-show <bool>",
              example=["cli: -show",
                      "api: chip.set('option','show',True)"],
              schelp="""
              Specifies that the final hardware layout should be
              shown after the compilation has been completed. The
              final layout and tool used to display the layout is
              flow dependent.""")
  
      scparam(cfg, ['option', 'autoinstall'],
              sctype='bool',
              shorthelp=f"Option: auto install packages",
              switch=f"-autoinstall <bool>",
              example=[
                  f"cli: -autoinstall true'",
                  f"api: chip.set('option', 'autoinstall', True)"],
              schelp=f"""
              Enables automatic installation of missing dependencies from
              the registry.""")
  
      scparam(cfg, ['option', 'registry'],
              sctype='[dir]',
              shorthelp=f"Option: package registry",
              switch=f"-registry <dir>",
              example=[
                  f"cli: -registry '~/myregistry'",
                  f"api: chip.set('option','registry','~/myregistry')"],
              schelp=f"""
              List of Silicon Unified Packager (SUP) registry directories.
              Directories can be local file system folders or
              publicly available registries served up over http. The naming
              convention for registry packages is:
              <name>/<name>-<version>.json(.<gz>)?
              """)
  
      scparam(cfg,['option', 'entrypoint'],
              sctype='str',
              shorthelp="Program entry point",
              switch="-entrypoint <str>",
              example=["cli: -entrypoint top",
                      "api: chip.set('option', 'entrypoint', 'top')"],
              schelp="""Alternative entrypoint for compilation and
              simulation. The default entry point is 'design'.""")
  
      scparam(cfg,['option', 'idir'],
              sctype='[dir]',
              shorthelp="Design search paths",
              switch=['+incdir+<dir>', '-I <dir>'],
              example=[
                  "cli: +incdir+./mylib",
                  "api: chip.set('option','idir','./mylib')"],
              schelp="""
              Search paths to look for files included in the design using
              the ```include`` statement.""")
  
      scparam(cfg,['option', 'ydir'],
              sctype='[dir]',
              shorthelp="Design module search paths",
              switch='-y <dir>',
              example=[
                  "cli: -y './mylib'",
                  "api: chip.set('option','ydir','./mylib')"],
              schelp="""
              Search paths to look for verilog modules found in the the
              source list. The import engine will look for modules inside
              files with the specified +libext+ param suffix.""")
  
      scparam(cfg,['option', 'vlib'],
              sctype='[file]',
              shorthelp="Design libraries",
              switch='-v <file>',
              example=["cli: -v './mylib.v'",
                       "api: chip.set('option', 'vlib','./mylib.v')"],
              schelp="""
              List of library files to be read in. Modules found in the
              libraries are not interpreted as root modules.""")
  
      scparam(cfg,['option', 'define'],
              sctype='[str]',
              shorthelp="Design pre-processor symbol",
              switch="-D<str>",
              example=["cli: -DCFG_ASIC=1",
                       "api: chip.set('option','define','CFG_ASIC=1')"],
              schelp="""Symbol definition for source preprocessor.""")
  
      scparam(cfg,['option', 'libext'],
              sctype='[str]',
              shorthelp="Design file extensions",
              switch="+libext+<str>",
              example=[
                  "cli: +libext+sv",
                  "api: chip.set('option','libext','sv')"],
              schelp="""
              List of file extensions that should be used for finding modules.
              For example, if -y is specified as ./lib", and '.v' is specified as
              libext then the files ./lib/\\*.v ", will be searched for
              module matches.""")
  
      name = 'default'
      scparam(cfg,['option', 'param', name],
              sctype='str',
              shorthelp="Design parameter",
              switch="-param 'name <str>'",
              example=[
                  "cli: -param 'N 64'",
                  "api: chip.set('option','param','N','64')"],
              schelp="""
              Sets a top verilog level design module parameter. The value
              is limited to basic data literals. The parameter override is
              passed into tools such as Verilator and Yosys. The parameters
              support Verilog integer literals (64'h4, 2'b0, 4) and strings.
              Name of the top level module to compile.""")
  
  
      scparam(cfg,['option', 'cmdfile'],
              sctype='[file]',
              shorthelp="Design compilation command file",
              switch='-f <file>',
              example=["cli: -f design.f",
                       "api: chip.set('option', 'cmdfile','design.f')"],
              schelp="""
              Read the specified file, and act as if all text inside it was specified
              as command line parameters. Supported by most verilog simulators
              including Icarus and Verilator. The format of the file is not strongly
              standardized. Support for comments and environment variables within
              the file varies and depends on the tool used. SC simply passes on
              the filepath toe the tool executable.""")
  
      scparam(cfg,['option', 'flowcontinue'],
              sctype='bool',
              shorthelp="Continue flow on error",
              switch='-flowcontinue',
              example=["cli: -flowcontinue",
                       "api: chip.set('option', 'flowcontinue', True)"],
              schelp="""
              Continue executing flow after a tool logs errors. The default
              behavior is to quit executing the flow if a task ends and the errors
              metric is greater than 0. Note that the flow will always cease
              executing if the tool returns a nonzero status code. """)
  
      return cfg
  
  ############################################
  # Package information
  ############################################
  
  def schema_package(cfg):
  
      userid = 'default'
      module = 'default'
  
      scparam(cfg, ['package', 'depgraph', module],
              sctype='[(str,str)]',
              scope='global',
              shorthelp=f"Package dependency list",
              switch=f"-package_depgraph 'module <(str,str)>'",
              example=[
                  f"cli: -package_depgraph 'top (cpu,1.0.1)'",
                  f"api: chip.set('package','depgraph','top',('cpu','1.0.1'))"],
              schelp=f"""
              List of Silicon Unified Packager (SUP) dependencies
              used by the design specified on a per module basis a
              list of string tuples ('name','version').""")
  
      scparam(cfg,['package', 'name'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: name",
              switch=f"-package_name <str>",
              example=[
                  f"cli: -package_name yac",
                  f"api: chip.set('package','name','yac')"],
              schelp=f"""Package name.""")
  
      scparam(cfg,['package', 'version'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: version",
              switch=f"-package_version <str>",
              example=[
                  f"cli: -package_version 1.0",
                  f"api: chip.set('package','version','1.0')"],
              schelp=f"""Package version. Can be a branch, tag, commit hash,
              or a semver compatible version.""")
  
      scparam(cfg,['package', 'description'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: description",
              switch=f"-package_description <str>",
              example=[
                  f"cli: -package_description 'Yet another cpu'",
                  f"api: chip.set('package','description','Yet another cpu')"],
              schelp=f"""Package short one line description for package
              managers and summary reports.""")
  
      scparam(cfg,['package', 'keyword'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: keyword",
              switch=f"-package_keyword <str>",
              example=[
                  f"cli: -package_keyword cpu",
                  f"api: chip.set('package','keyword','cpu')"],
              schelp=f"""Package keyword(s) used to characterize package.""")
  
      scparam(cfg,['package', 'homepage'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: project homepage",
              switch=f"-package_homepage <str>",
              example=[
                  f"cli: -package_homepage index.html",
                  f"api: chip.set('package','homepage','index.html')"],
              schelp=f"""Package homepage.""")
  
      scparam(cfg,['package', 'doc', 'homepage'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: documentation homepage",
              switch=f"-package_doc_homepage <str>",
              example=[
                  f"cli: -package_doc_homepage index.html",
                  f"api: chip.set('package','doc', 'homepage','index.html')"],
              schelp=f"""
              Package documentation homepage. Filepath to design docs homepage.
              Complex designs can can include a long non standard list of
              documents dependent.  A single html entry point can be used to
              present an organized documentation dashboard to the designer.""")
  
      doctypes = ['datasheet',
                  'reference',
                  'userguide',
                  'quickstart',
                  'releasenotes',
                  'testplan',
                  'signoff',
                  'tutorial']
  
      for item in doctypes:
          scparam(cfg,['package', 'doc', item],
              sctype='[file]',
              scope='global',
              shorthelp=f"Package: {item} document",
              switch=f"-package_doc_{item} <str",
              example=[
                  f"cli: -package_doc_{item} {item}.pdf",
                  f"api: chip.set('package','doc',{item},'{item}.pdf')"],
              schelp=f""" Package list of {item} documents.""")
  
      scparam(cfg,['package', 'repo'],
              sctype='[str]',
              scope='global',
              shorthelp=f"Package: code repository",
              switch=f"-package_repo <str>",
              example=[
                  f"cli: -package_repo 'git@github.com:aolofsson/oh.git'",
                  f"api: chip.set('package','repo','git@github.com:aolofsson/oh.git')"],
              schelp=f"""Package IP address to source code repository.""")
  
      scparam(cfg,['package', 'dependency', module],
              sctype='[str]',
              scope='global',
              shorthelp=f"Package: version dependancies",
              switch=f"-package_dependency 'module <str>'",
              example=[
                  f"cli: -package_dependency 'hello 1.0'",
                  f"api: chip.set('package','dependency','hello','1.0')"],
              schelp=f"""Package dependencies specified as a key value pair.
              Versions shall follow the semver standard.""")
  
      scparam(cfg,['package', 'target'],
              sctype='[str]',
              scope='global',
              shorthelp=f"Package: qualified targets",
              switch=f"-package_target <str>",
              example=[
                  f"cli: -package_target 'asicflow_freepdk45'",
                  f"api: chip.set('package','target','asicflow_freepdk45')"],
              schelp=f"""Package list of qualified compilation targets.""")
  
      scparam(cfg,['package', 'license'],
              sctype='[str]',
              scope='global',
              shorthelp=f"Package: license identifiers",
              switch=f"-package_license <str>",
              example=[
                  f"cli: -package_license 'Apache-2.0'",
                  f"api: chip.set('package','license','Apache-2.0')"],
              schelp=f"""Package list of SPDX license identifiers.""")
  
      scparam(cfg,['package', 'licensefile'],
              sctype='[file]',
              scope='global',
              shorthelp=f"Package: license files",
              switch=f"-package_licensefile <file>",
              example=[
                  f"cli: -package_licensefile './LICENSE'",
                  f"api: chip.set('package','licensefile','./LICENSE')"],
              schelp=f"""Package list of license files for to be
              applied in cases when a SPDX identifier is not available.
              (eg. proprietary licenses).list of SPDX license identifiers.""")
  
      scparam(cfg,['package', 'location'],
              sctype='[str]',
              scope='global',
              shorthelp=f"Package: location",
              switch=f"-package_location <file>",
              example=[
                  f"cli: -package_location 'mars'",
                  f"api: chip.set('package','location','mars')"],
              schelp=f"""Package country of origin specified as standardized
              international country codes. The field can be left blank
              if the location is unknown or global.""")
  
      scparam(cfg,['package', 'organization'],
              sctype='[str]',
              scope='global',
              shorthelp=f"Package: sponsoring organization",
              switch=f"-package_organization <str>",
              example=[
                  f"cli: -package_organization 'humanity'",
                  f"api: chip.set('package','organization','humanity')"],
              schelp=f"""Package sponsoring organization. The field can be left
              blank if not applicable.""")
  
      scparam(cfg,['package', 'publickey'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: public key",
              switch=f"-package_publickey <str>",
              example=[
                  f"cli: -package_publickey '6EB695706EB69570'",
                  f"api: chip.set('package','publickey','6EB695706EB69570')"],
              schelp=f"""Package public project key.""")
  
      record = ['name',
                'email',
                'username',
                'location',
                'organization',
                'publickey']
  
      for item in record:
          scparam(cfg,['package', 'author', userid, item],
                  sctype='str',
                  scope='global',
                  shorthelp=f"Package: author {item}",
                  switch=f"-package_author_{item} 'userid <str>'",
                  example=[
                      f"cli: -package_author_{item} 'wiley wiley@acme.com'",
                      f"api: chip.set('package','author','wiley','{item}','wiley@acme.com')"],
                  schelp=f"""Package author {item} provided with full name as key and
                  {item} as value.""")
  
      return cfg
  
  ############################################
  # Design Checklist
  ############################################
  
  def schema_checklist(cfg):
  
      item = 'default'
      standard = 'default'
      metric = 'default'
  
      scparam(cfg,['checklist', standard, item, 'description'],
              sctype='str',
              scope='global',
              shorthelp="Checklist: item description",
              switch="-checklist_description 'standard item <str>",
              example=[
                  "cli: -checklist_description 'ISO D000 A-DESCRIPTION'",
                  "api: chip.set('checklist','ISO','D000','description','A-DESCRIPTION')"],
              schelp="""
              A short one line description of the checklist item.""")
  
      scparam(cfg,['checklist', standard, item, 'requirement'],
              sctype='str',
              scope='global',
              shorthelp="Checklist: item requirement",
              switch="-checklist_requirement 'standard item <str>",
              example=[
                  "cli: -checklist_requirement 'ISO D000 DOCSTRING'",
                  "api: chip.set('checklist','ISO','D000','requirement','DOCSTRING')"],
              schelp="""
              A complete requirement description of the checklist item
              entered as a multi-line string.""")
  
      scparam(cfg,['checklist', standard, item, 'dataformat'],
              sctype='str',
              scope='global',
              shorthelp="Checklist: item data format",
              switch="-checklist_dataformat 'standard item <float>'",
              example=[
                  "cli: -checklist_dataformat 'ISO D000 dataformat README'",
                  "api: chip.set('checklist','ISO','D000','dataformat','README')"],
              schelp="""
              Free text description of the type of data files acceptable as
              checklist signoff validation.""")
  
      scparam(cfg,['checklist', standard, item, 'rationale'],
              sctype='[str]',
              scope='global',
              shorthelp="Checklist: item rational",
              switch="-checklist_rationale 'standard item <str>",
              example=[
                  "cli: -checklist_rational 'ISO D000 reliability'",
                  "api: chip.set('checklist','ISO','D000','rationale','reliability')"],
              schelp="""
              Rationale for the the checklist item. Rationale should be a
              unique alphanumeric code used by the standard or a short one line
              or single word description.""")
  
      scparam(cfg,['checklist', standard, item, 'criteria'],
              sctype='[str]',
              scope='global',
              shorthelp="Checklist: item criteria",
              switch="-checklist_criteria 'standard item <float>'",
              example=[
                  "cli: -checklist_criteria 'ISO D000 errors==0'",
                  "api: chip.set('checklist','ISO','D000','criteria','errors==0')"],
              schelp="""
              Simple list of signoff criteria for checklist item which
              must all be met for signoff. Each signoff criteria consists of
              a metric, a relational operator, and a value in the form.
              'metric op value'.""")
  
      scparam(cfg,['checklist', standard, item, 'task'],
              sctype='[(str,str,str)]',
              scope='global',
              shorthelp="Checklist: item task",
              switch="-checklist_task 'standard item <(str, str, str)>'",
              example=[
                  "cli: -checklist_task 'ISO D000 (job0,place,0)'",
                  "api: chip.set('checklist','ISO','D000','task',('job0','place','0'))"],
              schelp="""
              Flowgraph job and task used to verify the checklist item.
              The parameter should be left empty for manual and for tool
              flows that bypass the SC infrastructure.""")
  
      scparam(cfg,['checklist', standard, item, 'report'],
              sctype='[file]',
              scope='global',
              shorthelp="Checklist: item report",
              switch="-checklist_report 'standard item <file>'",
              example=[
                  "cli: -checklist_report 'ISO D000 my.rpt'",
                  "api: chip.set('checklist','ISO','D000','report','my.rpt')"],
              schelp="""
              Filepath to report(s) of specified type documenting the successful
              validation of the checklist item.""")
  
      scparam(cfg,['checklist', standard, item, 'waiver', metric],
              sctype='[file]',
              scope='global',
              shorthelp="Checklist: item metric waivers",
              switch="-checklist_waiver 'standard item metric <file>'",
              example=[
                  "cli: -checklist_waiver 'ISO D000 bold my.txt'",
                  "api: chip.set('checklist','ISO','D000','waiver','hold', 'my.txt')"],
              schelp="""
              Filepath to report(s) documenting waivers for the checklist
              item specified on a per metric basis.""")
  
      scparam(cfg,['checklist', standard, item, 'ok'],
              sctype='bool',
              scope='global',
              shorthelp="Checklist: item ok",
              switch="-checklist_ok 'standard item <str>'",
              example=[
                  "cli: -checklist_ok 'ISO D000 true'",
                  "api: chip.set('checklist','ISO','D000','ok', True)"],
              schelp="""
              Boolean check mark for the checklist item. A value of
              True indicates a human has inspected the all item dictionary
              parameters check out.""")
  
      return cfg
  
  ###########################
  # ASIC Setup
  ###########################
  
  def schema_asic(cfg):
      '''ASIC Automated Place and Route Parameters'''
  
      step = 'default'
      index = 'default'
  
      scparam(cfg, ['asic', 'stackup'],
              sctype='str',
              scope='job',
              shorthelp="ASIC Stackup target",
              switch="-asic_stackup <str>",
              example=["cli: -asic_stackup 2MA4MB2MC",
                       "api: chip.set('asic','stackup','2MA4MB2MC')"],
              schelp="""
              Target ASIC stackup to use in the design. The stackup is required
              parameter for PDKs with multiple metal stackups.""")
  
      scparam(cfg, ['asic', 'pdk'],
              sctype='str',
              scope='job',
              shorthelp="ASIC PDK target",
              switch="-asic_pdk <str>",
              example=["cli: -asic_pdk freepdk45",
                       "api: chip.set('asic','pdk','freepdk45')"],
              schelp="""
              Target ASIC PDK to use in the design.""")
  
      scparam(cfg, ['asic', 'logiclib'],
              sctype='[str]',
              scope='job',
              shorthelp="ASIC: logic libraries",
              switch="-asic_logiclib <str>",
              example=["cli: -asic_logiclib nangate45",
                       "api: chip.set('asic', 'logiclib','nangate45')"],
              schelp="""List of all selected logic libraries libraries
              to use for optimization for a given library architecture
              (9T, 11T, etc).""")
  
      scparam(cfg, ['asic', 'macrolib'],
              sctype='[str]',
              scope='job',
              shorthelp="ASIC: macro libraries",
              switch="-asic_macrolib <str>",
              example=["cli: -asic_macrolib sram64x1024",
                       "api: chip.set('asic', 'macrolib','sram64x1024')"],
              schelp="""
              List of macro libraries to be linked in during synthesis and place
              and route. Macro libraries are used for resolving instances but are
              not used as targets for logic synthesis.""")
  
      scparam(cfg, ['asic', 'delaymodel'],
              sctype='str',
              scope='job',
              shorthelp="ASIC: delay model",
              switch="-asic_delaymodel <str>",
              example= ["cli: -asic_delaymodel ccs",
                        "api: chip.set('asic', 'delaymodel', 'ccs')"],
              schelp="""
              Delay model to use for the target libs. Supported values
              are nldm and ccs.""")
  
      net = 'default'
      scparam(cfg, ['asic', 'ndr', net],
              sctype='(float,float)',
              scope='job',
              shorthelp="ASIC: non-default routing rule",
              switch="-asic_ndr 'netname <(float,float)>",
              example= ["cli: -asic_ndr 'clk (0.2,0.2)'",
                      "api: chip.set('asic','ndr','clk', (0.2,0.2))"],
              schelp="""
              Definitions of non-default routing rule specified on a per
              net basis. Constraints are entered as a (width,space) tuples
              specified in microns.""")
  
      scparam(cfg, ['asic', 'minlayer'],
              sctype='str',
              scope='job',
              shorthelp="ASIC: minimum routing layer",
              switch="-asic_minlayer <str>",
              example= ["cli: -asic_minlayer m2",
                      "api: chip.set('asic', 'minlayer', 'm2')"],
              schelp="""
              Minimum SC metal layer name to be used for automated place and route .
              Alternatively the layer can be a string that matches a layer hard coded
              in the pdk_aprtech file. Designers wishing to use the same setup across
              multiple process nodes should use the integer approach. For processes
              with ambiguous starting routing layers, exact strings should be used.
              """)
  
      scparam(cfg, ['asic', 'maxlayer'],
              sctype='str',
              scope='job',
              shorthelp="ASIC: maximum routing layer",
              switch="-asic_maxlayer <str>",
              example= ["cli: -asic_maxlayer m2",
                      "api: chip.set('asic', 'maxlayer', 'm2')"],
              schelp="""
              Maximum SC metal layer name to be used for automated place and route .
              Alternatively the layer can be a string that matches a layer hard coded
              in the pdk_aprtech file. Designers wishing to use the same setup across
              multiple process nodes should use the integer approach. For processes
              with ambiguous starting routing layers, exact strings should be used.
              """)
  
      scparam(cfg, ['asic', 'maxfanout'],
              sctype='int',
              scope='job',
              shorthelp="ASIC: maximum fanout",
              switch="-asic_maxfanout <int>",
              example= ["cli: -asic_maxfanout 64",
                      "api: chip.set('asic', 'maxfanout', '64')"],
              schelp="""
               Maximum driver fanout allowed during automated place and route.
              The parameter directs the APR tool to break up any net with fanout
              larger than maxfanout into sub nets and buffer.""")
  
      scparam(cfg, ['asic', 'maxlength'],
              sctype='float',
              unit='um',
              scope='job',
              shorthelp="ASIC: maximum wire length",
              switch="-asic_maxlength <float>",
              example= ["cli: -asic_maxlength 1000",
                      "api: chip.set('asic', 'maxlength', '1000')"],
              schelp="""
              Maximum total wire length allowed in design during APR. Any
              net that is longer than maxlength is broken up into segments by
              the tool.""")
  
      scparam(cfg, ['asic', 'maxcap'],
              sctype='float',
              unit='F',
              scope='job',
              shorthelp="ASIC: maximum net capacitance",
              switch="-asic_maxcap <float>",
              example= ["cli: -asic_maxcap '0.25e-12'",
                        "api: chip.set('asic', 'maxcap', '0.25e-12')"],
              schelp="""Maximum allowed capacitance per net.""")
  
      scparam(cfg, ['asic', 'maxslew'],
              sctype='float',
              unit='s',
              scope='job',
              shorthelp="ASIC: maximum slew",
              switch="-asic_maxslew <float>",
              example= ["cli: -asic_maxslew '0.25e-9'",
                      "api: chip.set('asic', 'maxslew', '0.25e-9')"],
              schelp="""Maximum allowed transition time per net.""")
  
      sigtype='default'
      scparam(cfg, ['asic', 'rclayer', sigtype],
              sctype='str',
              scope='job',
              shorthelp="ASIC: parasitics layer",
              switch="-asic_rclayer 'sigtype <str>'",
              example= ["cli: -asic_rclayer 'clk m3'",
                      "api: chip.set('asic', 'rclayer', 'clk', 'm3')"],
              schelp="""
              Technology agnostic metal layer to be used for parasitic
              extraction estimation during APR for the wire type specified
              Current the supported wire types are: clk, data. The metal
              layers can be specified as technology agnostic SC layers
              starting with m1 or as hard PDK specific layer names.""")
  
      scparam(cfg, ['asic', 'vpinlayer'],
              sctype='str',
              scope='job',
              shorthelp="ASIC: vertical pin layer",
              switch="-asic_vpinlayer <str>",
              example= ["cli: -asic_vpinlayer m3",
                      "api: chip.set('asic', 'vpinlayer', 'm3')"],
              schelp="""
              Metal layer to use for automated vertical pin placement
              during APR.  The metal layers can be specified as technology
              agnostic SC layers starting with m1 or as hard PDK specific
              layer names.""")
  
      scparam(cfg, ['asic', 'hpinlayer'],
              sctype='str',
              scope='job',
              shorthelp="ASIC: vertical pin layer",
              switch="-asic_hpinlayer <str>",
              example= ["cli: -asic_hpinlayer m4",
                      "api: chip.set('asic', 'hpinlayer', 'm4')"],
              schelp="""
              Metal layer to use for automated horizontal pin placement
              during APR.  The metal layers can be specified as technology
              agnostic SC layers starting with m1 or as hard PDK specific
              layer names.""")
  
      scparam(cfg, ['asic', 'density'],
              sctype='float',
              scope='job',
              shorthelp="ASIC: target core density",
              switch="-asic_density <float>",
              example= ["cli: -asic_density 30",
                        "api: chip.set('asic', 'density', '30')"],
              schelp="""
              Target density based on the total design cell area reported
              after synthesis. This number is used when no diearea or floorplan is
              supplied. Any number between 1 and 100 is legal, but values above 50
              may fail due to area/congestion issues during apr.""")
  
      scparam(cfg, ['asic', 'coremargin'],
              sctype='float',
              unit='um',
              scope='job',
              shorthelp="ASIC: block core margin",
              switch="-asic_coremargin <float>",
              example= ["cli: -asic_coremargin 1",
                        "api: chip.set('asic', 'coremargin', '1')"],
              schelp="""
              Halo/margin between the die boundary and core placement for
              automated floorplanning when no diearea or floorplan is
              supplied.""")
  
      scparam(cfg, ['asic', 'aspectratio'],
              sctype='float',
              scope='job',
              shorthelp="ASIC: block aspect ratio",
              switch="-asic_aspectratio <float>",
              example= ["cli: -asic_aspectratio 2.0",
                      "api: chip.set('asic', 'aspectratio', '2.0')"],
              schelp="""
              Height to width ratio of the block for automated floor-planning.
              Values below 0.1 and above 10 should be avoided as they will likely fail
              to converge during placement and routing. The ideal aspect ratio for
              most designs is 1. This value is only used when no diearea or floorplan
              is supplied.""")
  
      scparam(cfg, ['asic', 'diearea'],
              sctype='[(float,float)]',
              unit='um',
              scope='job',
              shorthelp="ASIC: die area outline",
              switch="-asic_diearea <[(float,float)]>",
              example= ["cli: -asic_diearea '(0,0)'",
                      "api: chip.set('asic', 'diearea', (0,0))"],
              schelp="""
              List of (x,y) points that define the outline of the die area for the
              physical design. Simple rectangle areas can be defined with two points,
              one for the lower left corner and one for the upper right corner. All
              values are specified in microns.""")
  
      scparam(cfg, ['asic', 'corearea'],
              sctype='[(float,float)]',
              unit='um',
              scope='job',
              shorthelp="ASIC: core area outline",
              switch="-asic_corearea <[(float,float)]>",
              example= ["cli: -asic_corearea '(0,0)'",
                      "api: chip.set('asic', 'corearea', (0,0))"],
              schelp="""
              List of (x,y) points that define the outline of the core area for the
              physical design. Simple rectangle areas can be defined with two points,
              one for the lower left corner and one for the upper right corner. All
              values are specified in microns.""")
  
      tool = 'default'
      key = 'default'
      scparam(cfg, ['asic', 'file', tool, key],
              sctype='[file]',
              shorthelp="ASIC: special file",
              switch="-asic_file 'tool key<file>'",
              example=[
                  "cli: -asic_file 'yosys presyn ~/presyn.tcl'",
                  "api: chip.set('asic','file','yosys','presyn','~/presyn.tcl')"],
              schelp="""
              List of named files specified on a per tool basis.
              The parameter should only be used for specifying files that are
              not directly supported by the ASIC schema.""")
  
      scparam(cfg, ['asic', 'dir', tool, key],
              sctype='[dir]',
              shorthelp="ASIC: special directory",
              switch="-asic_dir 'tool key <dir>'",
              example=[
                  "cli: -asic_dir 'lib atool db ~/libdb'",
                  "api: chip.set('asic','dir','atool','db','~/libdb')"],
              schelp="""
              List of named dirs specified on a per tool basis. The parameter
              should only be used for specifying dirs that are not directly
              supported by the ASIC schema.""")
  
      scparam(cfg, ['asic', 'var', tool, key],
              sctype='[str]',
              shorthelp="ASIC: special variable",
              switch="-asic_variable 'tool key <str>'",
              example=[
                  "cli: -asic_variable 'xyce modeltype bsim4'""",
                  "api: chip.set('asic','var','xyce','modeltype','bsim4')"],
              schelp="""
              List of key/value strings specified on a per basis. The parameter
              should only be used for specifying variables that are
              not directly supported by the SiliconCompiler PDK schema.""")
  
  
      # TODO: Expand on the exact definitions of these types of cells.
      # minimize typing
      names = ['driver',
               'load',
               'buf',
               'decap',
               'delay',
               'tie',
               'hold',
               'clkbuf',
               'clkdelay',
               'clkinv',
               'clkgate',
               'clkicg',
               'clklogic',
               'ignore',
               'filler',
               'tap',
               'endcap',
               'antenna']
  
      for item in names:
          scparam(cfg, ['asic', 'cells', item],
                  sctype='[str]',
                  shorthelp=f"ASIC: {item} cell list",
                  switch=f"-asic_cells_{item} '<str>'",
                  example=[
                      f"cli: -asic_cells_{item} '*eco*'",
                      f"api: chip.set('asic','cells',{item},'*eco*')"],
                  schelp="""
                  List of cells grouped by a property that can be accessed
                  directly by the designer and tools. The example below shows how
                  all cells containing the string 'eco' could be marked as dont use
                  for the tool.""")
  
      # Place and route parameters (optional)
      scparam(cfg, ['asic', 'pgmetal'],
              sctype='str',
              shorthelp="ASIC: powergrid layer",
              switch="-asic_pgmetal '<str>'",
              example=["cli: -asic_pgmetal m1",
                      "api: chip.set('asic','pgmetal','m1')"],
              schelp="""
              Top metal layer used for power and ground routing within the
              library. The parameter can be used to guide cell power grid
              hookup by APR tools.""")
  
      scparam(cfg,['asic', 'libarch'],
              sctype='str',
              shorthelp="ASIC: library architecture",
              switch="-asic_libarch '<str>'",
              example=[
                  "cli: -asic_libarch '12track'",
                  "api: chip.set('asic','libarch','12track')"],
              schelp="""
              The library architecture (e.g. library height) used to build the
              design. For example a PDK with support for 9 and 12 track libraries
              might have 'libarchs' called 9t and 12t.""")
  
      # footprint
      key = 'default'
      scparam(cfg,['asic', 'footprint', key, 'alias'],
              sctype='[str]',
              shorthelp="ASIC: Footprint name aliases",
              switch="-asic_footprint_alias 'key <str>'",
              example=[
                  "cli: -asic_footprint_alias '12track FreeCell'",
                  "api: chip.set('asic','footprint','12track','alias','FreeCell')"],
              schelp="""
              Alias for the footprint key that is sometimes needed when the footprint can
              be referenced by multiple names. The key is the 'official' footprint.""")
  
      scparam(cfg, ['asic', 'footprint', key, 'symmetry'],
              sctype='str',
              shorthelp="ASIC: Footprint symmetry",
              switch="-asic_footprint_symmetry 'key <str>'",
              example=[
                  "cli: -asic_footprint_symmetry 'core X Y'",
                  "api: chip.set('asic','footprint','core','symmetry','X Y')"],
              schelp="""
              Footprint symmetry based on LEF standard definition. 'X' implies
              symmetric about the x axis, 'Y' implies symmetry about the y axis, and
              'X Y' implies symmetry about the x and y axis.""")
  
  
      scparam(cfg, ['asic', 'footprint', key, 'size'],
              sctype='(float,float)',
              shorthelp="ASIC: Footprint size",
              switch="-asic_footprint_size 'key <str>'",
              example=[
                  "cli: -asic_footprint_size 'core (1.0,1.0)'",
                  "api: chip.set('asic','footprint','core','size',(1.0,1.0))"],
              schelp="""
              Size of the footprint described as a (width, height) tuple in
              microns.""")
  
  
      return cfg
  
  ############################################
  # Constraints
  ############################################
  
  def schema_constraint(cfg, scenario='default'):
  
      scparam(cfg,['constraint', scenario, 'voltage'],
              sctype='float',
              unit='V',
              scope='job',
              shorthelp="Constraint voltage level",
              switch="-constraint_voltage 'scenario <float>'",
              example=["cli: -constraint_voltage 'worst 0.9'",
                       "api: chip.set('constraint', 'worst','voltage', '0.9')"],
              schelp="""Operating voltage applied to the scenario.""")
  
      scparam(cfg,['constraint', scenario, 'temperature'],
              sctype='float',
              scope='job',
              shorthelp="Constraint temperature",
              switch="-constraint_temperature 'scenario <float>'",
              example=["cli: -constraint_temperature 'worst 125'",
                       "api: chip.set('constraint', 'worst', 'temperature','125')"],
              schelp="""Chip temperature applied to the scenario specified in degrees C.""")
  
      scparam(cfg,['constraint', scenario, 'libcorner'],
              sctype='str',
              scope='job',
              shorthelp="Constraint library corner",
              switch="-constraint_libcorner 'scenario <str>'",
              example=["cli: -constraint_libcorner 'worst ttt'",
                      "api: chip.set('constraint', 'worst', 'libcorner', 'ttt')"],
              schelp="""Library corner applied to the scenario to scale
              library timing models based on the libcorner value for models
              that support it. The parameter is ignored for libraries that
              have one hard coded model per libcorner.""")
  
      scparam(cfg,['constraint', scenario, 'pexcorner'],
              sctype='str',
              scope='job',
              shorthelp="Constraint pex corner",
              switch="-constraint_pexcorner 'scenario <str>'",
              example=["cli: -constraint_pexcorner 'worst max'",
                      "api: chip.set('constraint', 'worst', 'pexcorner', 'max')"],
              schelp="""Parasitic corner applied to the scenario. The
              'pexcorner' string must match a corner found in the pdk
              pexmodel setup.""")
  
      scparam(cfg,['constraint', scenario, 'opcond'],
              sctype='str',
              scope='job',
              shorthelp="Constraint operating condition",
              switch="-constraint_opcond 'scenario <str>'",
              example=["cli: -constraint_opcond 'worst typical_1.0'",
                       "api: chip.set('constraint', 'worst', 'opcond',  'typical_1.0')"],
              schelp="""Operating condition applied to the scenario. The value
              can be used to access specific conditions within the library
              timing models from the 'logiclib' timing models.""")
  
      scparam(cfg,['constraint', scenario, 'mode'],
              sctype='str',
              scope='job',
              shorthelp="Constraint operating mode",
              switch="-constraint_mode 'scenario <str>'",
              example=["cli: -constraint_mode 'worst test'",
                       "api: chip.set('constraint',  'worst','mode', 'test')"],
              schelp="""Operating mode for the scenario. Operating mode strings
              can be values such as test, functional, standby.""")
  
      scparam(cfg,['constraint', scenario, 'file'],
              sctype='[file]',
              scope='job',
              copy='true',
              shorthelp="Constraint files",
              switch="-constraint_file 'scenario <file>'",
              example=["cli: -constraint_file 'worst hello.sdc'",
                       "api: chip.set('constraint','worst','file', 'hello.sdc')"],
              schelp="""List of timing constraint files to use for the scenario. The
              values are combined with any constraints specified by the design
              'constraint' parameter. If no constraints are found, a default
              constraint file is used based on the clock definitions.""")
  
      scparam(cfg,['constraint', scenario, 'check'],
              sctype='[str]',
              scope='job',
              shorthelp="Constraint checks to perform",
              switch="-constraint_check 'scenario <str>'",
              example=["cli: -constraint_check 'worst check setup'",
                      "api: chip.add('constraint','worst','check','setup')"],
              schelp="""
              List of checks for to perform for the scenario. The checks must
              align with the capabilities of the EDA tools and flow being used.
              Checks generally include objectives like meeting setup and hold goals
              and minimize power. Standard check names include setup, hold, power,
              noise, reliability.""")
  
      return cfg
  
  ##############################################################################
  # Main routine
  if __name__ == "__main__":
      cfg = schema_cfg()
      print(json.dumps(cfg, indent=4, sort_keys=True))
  
files: siliconcompiler/schema.py
files: # Copyright 2020 Silicon Compiler Authors. All Rights Reserved.
  
  from siliconcompiler import utils
  
  import re
  import os
  import sys
  import copy as pycopy
  import json
  
  SCHEMA_VERSION = '0.9.0'
  
  #############################################################################
  # PARAM DEFINITION
  #############################################################################
  
  def scparam(cfg,
              keypath,
              sctype=None,
              require=None,
              defvalue=None,
              scope='job',
              copy='false',
              lock='false',
              hashalgo='sha256',
              signature=None,
              notes=None,
              unit=None,
              shorthelp=None,
              switch=None,
              example=None,
              schelp=None):
  
      # 1. decend keypath until done
      # 2. create key if missing
      # 3. populate leaf cell when keypath empty
      if keypath:
          key = keypath[0]
          keypath.pop(0)
          if key not in cfg.keys():
              cfg[key] = {}
          scparam(cfg[key],
                  keypath,
                  sctype=sctype,
                  scope=scope,
                  require=require,
                  defvalue=defvalue,
                  copy=copy,
                  lock=lock,
                  hashalgo=hashalgo,
                  signature=signature,
                  notes=notes,
                  unit=unit,
                  shorthelp=shorthelp,
                  switch=switch,
                  example=example,
                  schelp=schelp)
      else:
  
          # removing leading spaces as if schelp were a docstring
          schelp = utils.trim(schelp)
  
          # setting valus based on types
          # note (bools are never lists)
          if re.match(r'bool',sctype):
              require = 'all'
              if defvalue is None:
                  defvalue = 'false'
          if re.match(r'\[',sctype) and signature is None:
              signature = []
          if re.match(r'\[',sctype) and defvalue is None:
              defvalue = []
  
          # mandatory for all
          cfg['defvalue'] = defvalue
          cfg['value'] = pycopy.copy(defvalue)
          cfg['type'] = sctype
          cfg['scope'] = scope
          cfg['require'] = require
          cfg['lock'] = lock
          cfg['switch'] = switch
          cfg['shorthelp'] = shorthelp
          cfg['example'] = example
          cfg['help'] = schelp
          cfg['signature'] = signature
          cfg['notes'] = notes
  
          # unit for floats/ints
          if unit is not None:
              cfg['unit'] = unit
  
          # file only values
          if re.search(r'file',sctype):
              cfg['hashalgo'] = hashalgo
              cfg['copy'] = copy
              cfg['filehash'] = []
              cfg['date'] = []
              cfg['author'] = []
  
  
  #############################################################################
  # CHIP CONFIGURATION
  #############################################################################
  
  def schema_cfg():
      '''Method for defining Chip configuration schema
      All the keys defined in this dictionary are reserved words.
      '''
  
      # SC version number (bump on every non trivial change)
      # Version number following semver standard.
      # Software version syncs with SC releases (from _metadata)
  
      SCHEMA_VERSION = '0.8.0'
  
      # Basic schema setup
      cfg = {}
  
      # Place holder dictionaries updated by core methods()
      cfg['history'] = {}
      cfg['library'] = {}
  
      scparam(cfg,['schemaversion'],
              sctype='str',
              scope='global',
              defvalue=SCHEMA_VERSION,
              require='all',
              shorthelp="Schema version number",
              lock='true',
              switch="-schemaversion <str>",
              example=["api: chip.get('schemaversion')"],
              schelp="""SiliconCompiler schema version number.""")
  
      # Design topmodule/entrypoint
      scparam(cfg,['design'],
              sctype='str',
              scope='global',
              require='all',
              shorthelp="Design top module name",
              switch="-design <str>",
              example=["cli: -design hello_world",
                      "api: chip.set('design', 'hello_world')"],
              schelp="""Name of the top level module or library. Required for all
              chip objects.""")
  
      # input/output
      io = {'input': ['Input','true'],
            'output': ['Output','false']
      }
  
      filetype = 'default'
      for item, val in io.items():
          scparam(cfg,[item, filetype],
                  sctype='[file]',
                  copy=f"{val[1]}",
                  shorthelp=f"{val[0]} files",
                  switch=f"-{item} 'filetype <file>'",
                  example=[
                      f"cli: -{item} 'verilog hello_world.v'",
                      f"api: chip.set({item},'verilog','hello_world.v')"],
                  schelp=f"""
                  List of {item} files specifed by type. The filetype name must
                  align with the parameter names within the flow and tool setup
                  scripts. Examples of acceptable file types include python, c,
                  systemc, verilog, vhdl, netlist, def, gds, gerber, saif, sdc,
                  saif, vcd, spef, sdf.""")
  
      # Inputs and constraints
      cfg = schema_constraint(cfg)
  
      # Options
      cfg = schema_option(cfg)
      cfg = schema_arg(cfg)
      cfg = schema_unit(cfg)
  
      # Technology configuration
      cfg = schema_fpga(cfg)
      cfg = schema_asic(cfg)
      cfg = schema_pdk(cfg)
  
      # Flows
      cfg = schema_tool(cfg)
      cfg = schema_flowgraph(cfg)
  
      # Metrics
      cfg = schema_checklist(cfg)
      cfg = schema_metric(cfg)
      cfg = schema_record(cfg)
  
      # Modeling
      cfg = schema_model(cfg)
  
      # Datasheeet
      cfg = schema_datasheet(cfg)
  
      # Packaging
      cfg = schema_package(cfg)
  
      return cfg
  
  ###############################################################################
  # FPGA
  ###############################################################################
  
  def schema_fpga(cfg):
      ''' FPGA configuration
      '''
  
      scparam(cfg,['fpga', 'arch'],
              sctype='[file]',
              copy='true',
              shorthelp="FPGA: architecture file",
              switch="-fpga_arch <file>",
              example=["cli: -fpga_arch myfpga.xml",
                       "api:  chip.set('fpga', 'arch', 'myfpga.xml')"],
              schelp=""" Architecture definition file for FPGA place and route
              tool. For the VPR tool, the file is a required XML based description,
              allowing targeting a large number of virtual and commercial
              architectures. For most commercial tools, the fpga part name provides
              enough information to enable compilation and the 'arch' parameter is
              optional.""")
  
      scparam(cfg,['fpga', 'vendor'],
              sctype='str',
              shorthelp="FPGA: vendor name",
              switch="-fpga_vendor <str>",
              example=["cli: -fpga_vendor acme",
                      "api:  chip.set('fpga', 'vendor', 'acme')"],
              schelp="""
              Name of the FPGA vendor. The parameter is used to check part
              name and to select the eda tool flow in case 'edaflow' is
              unspecified.""")
  
      scparam(cfg,['fpga', 'partname'],
              sctype='str',
              require='fpga',
              shorthelp="FPGA: part name",
              switch="-fpga_partname <str>",
              example=["cli: -fpga_partname fpga64k",
                       "api:  chip.set('fpga', 'partname', 'fpga64k')"],
              schelp="""
              Complete part name used as a device target by the FPGA compilation
              tool. The part name must be an exact string match to the partname
              hard coded within the FPGA eda tool.""")
  
      scparam(cfg,['fpga', 'board'],
              sctype='str',
              shorthelp="FPGA: board name",
              switch="-fpga_board <str>",
              example=["cli: -fpga_board parallella",
                       "api:  chip.set('fpga', 'board', 'parallella')"],
              schelp="""
              Complete board name used as a device target by the FPGA compilation
              tool. The board name must be an exact string match to the partname
              hard coded within the FPGA eda tool. The parameter is optional and can
              be used in place of a partname and pin constraints for some tools.""")
  
      scparam(cfg,['fpga', 'program'],
              sctype='bool',
              shorthelp="FPGA: program enable",
              switch="-fpga_program <bool>",
              example=["cli: -fpga_program",
                       "api:  chip.set('fpga', 'program', True)"],
              schelp="""Specifies that the bitstream should be loaded into an FPGA.""")
  
      scparam(cfg,['fpga', 'flash'],
              sctype='bool',
              shorthelp="FPGA: flash enable",
              switch="-fpga_flash <bool>",
              example=["cli: -fpga_flash",
                       "api:  chip.set('fpga', 'flash', True)"],
              schelp="""Specifies that the bitstream should be flashed in the board/device.
              The default is to load the bitstream into volatile memory (SRAM).""")
  
      return cfg
  
  ###############################################################################
  # PDK
  ###############################################################################
  
  def schema_pdk(cfg, stackup='default'):
      ''' Process design kit configuration
      '''
  
      tool = 'default'
      filetype = 'default'
      pdkname = 'default'
  
      scparam(cfg, ['pdk', pdkname, 'foundry'],
              sctype='str',
              scope='global',
              require="asic",
              shorthelp="PDK: foundry name",
              switch="-pdk_foundry 'pdkname <str>'",
              example=["cli: -pdk_foundry 'asap7 virtual'",
                      "api:  chip.set('pdk', 'asap7', 'foundry', 'virtual')"],
              schelp="""
              Name of foundry corporation. Examples include intel, gf, tsmc,
              samsung, skywater, virtual. The \'virtual\' keyword is reserved for
              simulated non-manufacturable processes.""")
  
      scparam(cfg, ['pdk', pdkname, 'node'],
              sctype='float',
              scope='global',
              require="asic",
              shorthelp="PDK: process node",
              switch="-pdk_node 'pdkname <float>'",
              example=["cli: -pdk_node 'asap7 130'",
                      "api:  chip.set('pdk', 'asap7', 'node', 130)"],
              schelp="""
              Approximate relative minimum dimension of the process target specified
              in nanometers. The parameter is required for flows and tools that
              leverage the value to drive technology dependent synthesis and APR
              optimization. Node examples include 180, 130, 90, 65, 45, 32, 22 14,
              10, 7, 5, 3.""")
  
      scparam(cfg, ['pdk', pdkname, 'version'],
              sctype='str',
              scope='global',
              shorthelp="PDK: version",
              switch="-pdk_version 'pdkname <str>'",
              example=["cli: -pdk_version 'asap7 1.0'",
                      "api:  chip.set('pdk', 'asap7', 'version', '1.0')"],
              schelp="""
              Alphanumeric string specifying the version of the PDK. Verification of
              correct PDK and IP versions is a hard ASIC tapeout require in all
              commercial foundries. The version number can be used for design manifest
              tracking and tapeout checklists.""")
  
      scparam(cfg, ['pdk', pdkname, 'stackup'],
              sctype='[str]',
              scope='global',
              require='asic',
              shorthelp="PDK: metal stackups",
              switch="-pdk_stackup 'pdkname <str>'",
              example=["cli: -pdk_stackup 'asap7 2MA4MB2MC'",
                       "api: chip.add('pdk', 'asap7','stackup','2MA4MB2MC')"],
              schelp="""
              List of all metal stackups offered in the process node. Older process
              nodes may only offer a single metal stackup, while advanced nodes
              offer a large but finite list of metal stacks with varying combinations
              of metal line pitches and thicknesses. Stackup naming is unique to a
              foundry, but is generally a long string or code. For example, a 10
              metal stackup with two 1x wide, four 2x wide, and 4x wide metals,
              might be identified as 2MA4MB2MC, where MA, MB, and MC denote wiring
              layers with different properties (thickness, width, space). Each
              stackup will come with its own set of routing technology files and
              parasitic models specified in the pdk_pexmodel and pdk_aprtech
              parameters.""")
  
      scparam(cfg, ['pdk', pdkname, 'thickness', stackup],
              sctype='float',
              scope='global',
              unit='mm',
              shorthelp="PDK: unit thickness",
              switch="-pdk_thickness 'pdkname stackup <float>'",
              example=["cli: -pdk_thickness 'asap7 2MA4MB2MC 1.57'",
                      "api:  chip.set('pdk', 'asap7', 'thickness', '2MA4MB2MC', 1.57)"],
              schelp="""
              Thickness of a manfuatured unit specified on a per stackup basis.""")
  
      scparam(cfg, ['pdk', pdkname, 'wafersize'],
              sctype='float',
              scope='global',
              unit='mm',
              require="asic",
              shorthelp="PDK: wafer size",
              switch="-pdk_wafersize 'pdkname <float>'",
              example=["cli: -pdk_wafersize 'asap7 300'",
                      "api:  chip.set('pdk', 'asap7', 'wafersize', 300)"],
              schelp="""
              Wafer diameter used in wafer based manufacturing process. The standard diameter
              for leading edge manufacturing is 300mm. For older process technologies
              and specialty fabs, smaller diameters such as 200, 100, 125, 100 are common.
              The value is used to calculate dies per wafer and full factory chip costs.""")
  
      scparam(cfg, ['pdk', pdkname, 'panelsize'],
              sctype='[(float,float)]',
              scope='global',
              unit='mm',
              shorthelp="PDK: panel size",
              switch="-pdk_panelsize 'pdkname <float>'",
              example=["cli: -pdk_panelsize 'asap7 (45.72,60.96)'",
                      "api:  chip.set('pdk', 'asap7', 'panelsize', (45.72,60.96))"],
              schelp="""
              List of panel sizes supported in the manufacturing process.
              """)
  
      scparam(cfg, ['pdk', pdkname, 'unitcost'],
              sctype='float',
              scope='global',
              unit='USD',
              shorthelp="PDK: unit cost",
              switch="-pdk_unitcost 'pdkname <float>'",
              example=["cli: -pdk_unitcost 'asap7 10000'",
                       "api:  chip.set('pdk', 'asap7', 'unitcost', 10000)"],
              schelp="""
              Raw cost per unit shipped by the factory, not accounting for yield
              loss.""")
  
      scparam(cfg, ['pdk', pdkname, 'd0'],
              sctype='float',
              scope='global',
              shorthelp="PDK: process defect density",
              switch="-pdk_d0 'pdkname <float>'",
              example=["cli: -pdk_d0 'asap7 0.1'",
                       "api:  chip.set('pdk', 'asap7', 'd0', 0.1)"],
              schelp="""
              Process defect density (d0) expressed as random defects per cm^2. The
              value is used to calculate yield losses as a function of area, which in
              turn affects the chip full factory costs. Two yield models are
              supported: Poisson (default), and Murphy. The Poisson based yield is
              calculated as dy = exp(-area * d0/100). The Murphy based yield is
              calculated as dy = ((1-exp(-area * d0/100))/(area * d0/100))^2.""")
  
      scparam(cfg, ['pdk', pdkname, 'hscribe'],
              sctype='float',
              scope='global',
              unit='mm',
              shorthelp="PDK: horizontal scribe line width",
              switch="-pdk_hscribe 'pdkname <float>'",
              example=["cli: -pdk_hscribe 'asap7 0.1'",
                       "api:  chip.set('pdk', 'asap7', 'hscribe', 0.1)"],
              schelp="""
              Width of the horizontal scribe line used during die separation.
              The process is generally completed using a mechanical saw, but can be
              done through combinations of mechanical saws, lasers, wafer thinning,
              and chemical etching in more advanced technologies. The value is used
              to calculate effective dies per wafer and full factory cost.""")
  
      scparam(cfg, ['pdk', pdkname, 'vscribe'],
              sctype='float',
              scope='global',
              unit='mm',
              shorthelp="PDK: vertical scribe line width",
              switch="-pdk_vscribe 'pdkname <float>'",
              example=["cli: -pdk_vscribe 'asap7 0.1'",
                       "api:  chip.set('pdk', 'asap7', 'vscribe', 0.1)"],
              schelp="""
               Width of the vertical scribe line used during die separation.
              The process is generally completed using a mechanical saw, but can be
              done through combinations of mechanical saws, lasers, wafer thinning,
              and chemical etching in more advanced technologies. The value is used
              to calculate effective dies per wafer and full factory cost.""")
  
      scparam(cfg, ['pdk', pdkname, 'edgemargin'],
              sctype='float',
              scope='global',
              unit='mm',
              shorthelp="PDK: wafer edge keep-out margin",
              switch="-pdk_edgemargin 'pdkname <float>'",
              example=[
                  "cli: -pdk_edgemargin 'asap7 1'",
                  "api:  chip.set('pdk', 'asap7', 'edgemargin', 1)"],
              schelp="""
              Keep-out distance/margin from the edge inwards. The edge
              is prone to chipping and need special treatment that preclude
              placement of designs in this area. The edge value is used to
              calculate effective units per wafer/panel and full factory cost.""")
  
      scparam(cfg, ['pdk', pdkname, 'density'],
              sctype='float',
              scope='global',
              shorthelp="PDK: transistor density",
              switch="-pdk_density 'pdkname <float>'",
              example=["cli: -pdk_density 'asap7 100e6'",
                      "api:  chip.set('pdk', 'asap7', 'density', 10e6)"],
              schelp="""
              Approximate logic density expressed as # transistors / mm^2
              calculated as:
              0.6 * (Nand2 Transistor Count) / (Nand2 Cell Area) +
              0.4 * (Register Transistor Count) / (Register Cell Area)
              The value is specified for a fixed standard cell library within a node
              and will differ depending on the library vendor, library track height
              and library type. The value can be used to to normalize the effective
              density reported for the design across different process nodes. The
              value can be derived from a variety of sources, including the PDK DRM,
              library LEFs, conference presentations, and public analysis.""")
  
      simtype = 'default'
      scparam(cfg, ['pdk', pdkname, 'devmodel', tool, simtype, stackup],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: device models",
              switch="-pdk_devmodel 'pdkname tool simtype stackup <file>'",
              example=[
              "cli: -pdk_devmodel 'asap7 xyce spice M10 asap7.sp'",
              "api: chip.set('pdk','asap7','devmodel','xyce','spice','M10','asap7.sp')"],
              schelp="""
              List of filepaths to PDK device models for different simulation
              purposes and for different tools. Examples of device model types
              include spice, aging, electromigration, radiation. An example of a
              'spice' tool is xyce. Device models are specified on a per metal stack
              basis. Process nodes with a single device model across all stacks will
              have a unique parameter record per metal stack pointing to the same
              device model file.  Device types and tools are dynamic entries
              that depend on the tool setup and device technology. Pseud-standardized
              device types include spice, em (electromigration), and aging.""")
  
      corner='default'
      scparam(cfg, ['pdk', pdkname, 'pexmodel', tool, stackup, corner],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: parasitic TCAD models",
              switch="-pdk_pexmodel 'pdkname tool stackup corner <file>'",
              example=[
                  "cli: -pdk_pexmodel 'asap7 fastcap M10 max wire.mod'",
                  "api: chip.set('pdk','asap7','pexmodel','fastcap','M10','max','wire.mod')"],
              schelp="""
              List of filepaths to PDK wire TCAD models used during automated
              synthesis, APR, and signoff verification. Pexmodels are specified on
              a per metal stack basis. Corner values depend on the process being
              used, but typically include nomenclature such as min, max, nominal.
              For exact names, refer to the DRM. Pexmodels are generally not
              standardized and specified on a per tool basis. An example of pexmodel
              type is 'fastcap'.""")
  
      src = 'default'
      dst = 'default'
      scparam(cfg, ['pdk', pdkname, 'layermap', tool, src, dst, stackup],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: layer map file",
              switch="-pdk_layermap 'pdkname tool src dst stackup <file>'",
              example=[
                  "cli: -pdk_layermap 'asap7 klayout db gds M10 asap7.map'",
                  "api: chip.set('pdk','asap7','layermap','klayout','db','gds','M10','asap7.map')"],
              schelp="""
              Files describing input/output mapping for streaming layout data from
              one format to another. A foundry PDK will include an official layer
              list for all user entered and generated layers supported in the GDS
              accepted by the foundry for processing, but there is no standardized
              layer definition format that can be read and written by all EDA tools.
              To ensure mask layer matching, key/value type mapping files are needed
              to convert EDA databases to/from GDS and to convert between different
              types of EDA databases. Layer maps are specified on a per metal
              stackup basis. The 'src' and 'dst' can be names of SC supported tools
              or file formats (like 'gds').""")
  
  
      scparam(cfg, ['pdk', pdkname, 'display', tool, stackup],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: display file",
              switch="-pdk_display 'pdkname tool stackup <file>'",
              example=[
                  "cli: -pdk_display 'asap7 klayout M10 display.lyt'",
                  "api: chip.set('pdk','asap7','display','klayout','M10','display.cfg')"],
              schelp="""
              Display configuration files describing colors and pattern schemes for
              all layers in the PDK. The display configuration file is entered on a
              stackup and tool basis.""")
  
      #TODO: create firm list of accepted files
      libarch = 'default'
      scparam(cfg, ['pdk', pdkname, 'aprtech', tool, stackup, libarch, filetype],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: APR technology files",
              switch="-pdk_aprtech 'pdkname tool stackup libarch filetype <file>'",
              example=[
                  "cli: -pdk_aprtech 'asap7 openroad M10 12t lef tech.lef'",
                  "api: chip.set('pdk','asap7','aprtech','openroad','M10','12t','lef','tech.lef')"],
              schelp="""
              Technology file containing setup information needed to enable DRC clean APR
              for the specified stackup, libarch, and format. The 'libarch' specifies the
              library architecture (e.g. library height). For example a PDK with support
              for 9 and 12 track libraries might have 'libarchs' called 9t and 12t.
              The standard filetype for specifying place and route design rules for a
              process node is through a 'lef' format technology file. The
              'filetype' used in the aprtech is used by the tool specific APR TCL scripts
              to set up the technology parameters. Some tools may require additional
              files beyond the tech.lef file. Examples of extra file types include
              antenna, tracks, tapcell, viarules, em.""")
  
      checks = ['lvs', 'drc', 'erc', 'fill']
      name = 'default'
      for item in checks:
          scparam(cfg, ['pdk', pdkname, item, 'runset', tool, stackup, name],
                  sctype='[file]',
                  scope='global',
                  shorthelp=f"PDK: {item.upper()} runset files",
                  switch=f"-pdk_{item}_runset 'pdkname tool stackup name <file>'",
                  example=[
                      f"cli: -pdk_{item}_runset 'asap7 magic M10 basic $PDK/{item}.rs'",
                      f"api: chip.set('pdk', 'asap7','{item}','runset','magic','M10','basic','$PDK/{item}.rs')"],
                  schelp=f"""Runset files for {item.upper()} task.""")
  
          scparam(cfg, ['pdk', pdkname, item, 'waiver', tool, stackup, name],
                  sctype='[file]',
                  scope='global',
                  shorthelp=f"PDK: {item.upper()} waiver files",
                  switch=f"-pdk_{item}_waiver 'tool stackup name <file>'",
                  example=[
                      f"cli: -pdk_{item}_waiver 'asap7 magic M10 basic $PDK/{item}.txt'",
                      f"api: chip.set('pdk', 'asap7','{item}','waiver','magic','M10','basic','$PDK/{item}.txt')"],
                  schelp=f"""Waiver files for {item.upper()} task.""")
  
      ################
      # Routing grid
      ################
  
      layer = 'default'
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'name'],
              sctype='str',
              scope='global',
              shorthelp="PDK: routing grid name map",
              switch="-pdk_grid_name 'pdkname stackup layer <str>'",
              example=[
                  "cli: -pdk_grid_name 'asap7 M10 metal1 m1'",
                  "api: chip.set('pdk','asap7','grid','M10','metal1','name','m1')"],
              schelp="""
              Maps PDK metal names to the SC standardized layer stack
              starting with m1 as the lowest routing layer and ending
              with m<n> as the highest routing layer. The map is
              specified on a per metal stack basis.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'dir'],
              sctype='str',
              scope='global',
              shorthelp="PDK: routing grid preferred direction",
              switch="-pdk_grid_dir 'pdkname stackup layer <str>'",
              example=[
                  "cli: -pdk_grid_dir 'asap7 M10 m1 horizontal'",
                  "api: chip.set('pdk','asap7','grid','M10','m1','dir','horizontal')"],
              schelp="""
              Preferred routing direction specified on a per stackup
              and per metal basis. Valid routing directions are horizontal
              and vertical.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'xpitch'],
              sctype='float',
              scope='global',
              unit='um',
              shorthelp="PDK: routing grid vertical wire pitch",
              switch="-pdk_grid_xpitch 'pdkname stackup layer <float>'",
              example= [
                  "cli: -pdk_grid_xpitch 'asap7 M10 m1 0.5'",
                  "api: chip.set('pdk', 'asap7','grid','M10','m1','xpitch','0.5')"],
              schelp="""
              Defines the routing pitch for vertical wires on a per stackup and
              per metal basis.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'ypitch'],
              sctype='float',
              scope='global',
              unit='um',
              shorthelp="PDK: routing grid horizontal wire pitch",
              switch="-pdk_grid_ypitch 'pdkname stackup layer <float>'",
              example= [
                  "cli: -pdk_grid_ypitch 'asap7 M10 m1 0.5'",
                  "api: chip.set('pdk','asap7','grid','M10','m1','ypitch','0.5')"],
              schelp="""
              Defines the routing pitch for horizontal wires on a per stackup and
              per metal basis.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'xoffset'],
              sctype='float',
              scope='global',
              unit='um',
              shorthelp="PDK: routing grid vertical wire offset",
              switch="-pdk_grid_xoffset 'pdkname stackup layer <float>'",
              example= [
                  "cli: -pdk_grid_xoffset 'asap7 M10 m2 0.5'",
                  "api: chip.set('pdk','asap7','grid','M10','m2','xoffset','0.5')"],
              schelp="""
              Defines the grid offset of a vertical metal layer specified on a per
              stackup and per metal basis.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'yoffset'],
              sctype='float',
              scope='global',
              unit='um',
              shorthelp="PDK: routing grid horizontal wire offset",
              switch="-pdk_grid_yoffset 'pdkname stackup layer <float>'",
              example= [
                  "cli: -pdk_grid_yoffset 'asap7 M10 m2 0.5'",
                  "api: chip.set('pdk', 'asap7','grid','M10','m2','yoffset','0.5')"],
              schelp="""
              Defines the grid offset of a horizontal metal layer specified on a per
              stackup and per metal basis.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'adj'],
              sctype='float',
              scope='global',
              shorthelp="PDK: routing grid resource adjustment",
              switch="-pdk_grid_adj 'pdkname stackup layer <float>'",
              example= [
                  "cli: -pdk_grid_adj 'asap7 M10 m2 0.5'",
                  "api: chip.set('pdk','asap7','grid','M10','m2','adj','0.5')"],
              schelp="""
              Defines the routing resources adjustments for the design on a per layer
              basis. The value is expressed as a fraction from 0 to 1. A value of
              0.5 reduces the routing resources by 50%. If not defined, 100%
              routing resource utilization is permitted.""")
  
      corner='default'
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'cap', corner],
              sctype='float',
              scope='global',
              unit='ff/um',
              shorthelp="PDK: routing grid unit capacitance",
              switch="-pdk_grid_cap 'pdkname stackup layer corner <float>''",
              example= [
                  "cli: -pdk_grid_cap 'asap7 M10 m2 fast 0.2'",
                  "api: chip.set('pdk','asap7','grid','M10','m2','cap','fast','0.2')"],
              schelp="""
              Unit capacitance of a wire defined by the grid width and spacing values
              in the 'grid' structure specified on a per
              stackup, metal, and corner basis. As a rough rule of thumb, this value
              tends to stay around 0.2ff/um. This number should only be used for
              reality confirmation. Accurate analysis should use the PEX models.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'res', corner],
              sctype='float',
              scope='global',
              unit='ohm/um',
              shorthelp="PDK: routing grid unit resistance",
              switch="-pdk_grid_res 'pdkname stackup layer corner <float>''",
              example= [
                  "cli: -pdk_grid_res 'asap7 M10 m2 fast 0.2'",
                  "api: chip.set('pdk','asap7','grid','M10','m2','res','fast','0.2')"],
              schelp="""
              Resistance of a wire defined by the grid width and spacing values
              in the 'grid' structure specified as ohms/um on a per
              stackup, metal, and corner basis. The parameter is only meant to be
              used as a sanity check and for coarse design planning. Accurate
              analysis should use the TCAD PEX models.""")
  
      scparam(cfg, ['pdk', pdkname, 'grid', stackup, layer, 'tcr', corner],
              sctype='float',
              scope='global',
              unit='%/degree(T)',
              shorthelp="PDK: routing grid temperature coefficient",
              switch="-pdk_grid_tcr 'pdkname stackup layer corner <float>'",
              example= [
                  "cli: -pdk_grid_tcr 'asap7 M10 m2 fast 0.2'",
                  "api: chip.set('pdk','asap7','grid','M10','m2','tcr','fast','0.2')"],
              schelp="""
              Temperature coefficient of resistance of the wire defined by the grid
              width and spacing values in the 'grid' structure on a per stackup,
              layer, and corner basis. The number is only meant to be used as a
              sanity check and for coarse design planning. Accurate analysis
              should use the PEX models.""")
  
      ###############
      # EDA vars
      ###############
  
      key='default'
      scparam(cfg, ['pdk', pdkname, 'file', tool, key, stackup],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: special file",
              switch="-pdk_file 'pdkname tool key stackup <file>'",
              example=[
                  "cli: -pdk_file 'asap7 xyce spice M10 asap7.sp'",
                  "api: chip.set('pdk','asap7','file','xyce','spice','M10','asap7.sp')"],
              schelp="""
              List of named files specified on a per tool and per stackup basis.
              The parameter should only be used for specifying files that are
              not directly  supported by the SiliconCompiler PDK schema.""")
  
      scparam(cfg, ['pdk', pdkname, 'directory', tool, key, stackup],
              sctype='[dir]',
              scope='global',
              shorthelp="PDK: special directory",
              switch="-pdk_directory 'pdkname tool key stackup <file>'",
              example=[
                  "cli: -pdk_directory 'asap7 xyce rfmodel M10 rftechdir'",
                  "api: chip.set('pdk','asap7','directory','xyce','rfmodel','M10','rftechdir')"],
              schelp="""
              List of named directories specified on a per tool and per stackup basis.
              The parameter should only be used for specifying files that are
              not directly  supported by the SiliconCompiler PDK schema.""")
  
      scparam(cfg, ['pdk', pdkname, 'var', tool, key, stackup],
              sctype='[str]',
              scope='global',
              shorthelp="PDK: special variable",
              switch="-pdk_var 'pdkname tool stackup key <str>'",
              example=[
                  "cli: -pdk_var 'asap7 xyce modeltype M10 bsim4'""",
                  "api: chip.set('pdk','asap7','var','xyce','modeltype','M10','bsim4')"],
              schelp="""
              List of key/value strings specified on a per tool and per stackup basis.
              The parameter should only be used for specifying variables that are
              not directly  supported by the SiliconCompiler PDK schema.""")
  
      ###############
      # Docs
      ###############
  
      scparam(cfg,['pdk', pdkname, 'doc', 'homepage'],
              sctype='[file]',
              scope='global',
              shorthelp="PDK: documentation homepage",
              switch="-pdk_doc_homepage 'pdkname <file>'",
              example=["cli: -pdk_doc_homepage 'asap7 index.html'",
                       "api: chip.set('pdk','asap7','doc','homepage','index.html')"],
              schelp="""
              Filepath to PDK docs homepage. Modern PDKs can include tens or
              hundreds of individual documents. A single html entry point can
              be used to present an organized documentation dashboard to the
              designer.""")
  
      doctypes = ['datasheet',
                  'reference',
                  'userguide',
                  'install',
                  'quickstart',
                  'releasenotes',
                  'tutorial']
  
      for item in doctypes:
          scparam(cfg,['pdk', pdkname, 'doc', item],
                  sctype='[file]',
                  scope='global',
                  shorthelp=f"PDK: {item}",
                  switch= f"-pdk_doc_{item} 'pdkname <file>'",
                  example=[f"cli: -pdk_doc_{item} 'asap7 {item}.pdf'",
                           f"api: chip.set('pdk','asap7','doc',{item},'{item}.pdf')"],
                  schelp=f"""Filepath to {item} document.""")
  
      return cfg
  
  ###############################################################################
  # Design Modeling
  ###############################################################################
  
  def schema_model(cfg):
  
      corner = 'default'
      filetype = 'default'
      stackup =  'default'
  
      # functional
      scparam(cfg,['model', 'functional', filetype],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: Functional",
              switch=f"-model_functional 'filetype <file>'",
              example=[
                  f"cli: -model_functional 'systemc adder.sc'",
                  f"api: chip.set('model','functional','systemc','adder.sc')"],
              schelp=f"""
              Filepaths to (fast) functional models specified on a per filetype
              basis.""")
  
      # formal
      scparam(cfg,['model', 'formal', filetype],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: Formal",
              switch=f"-model_formal 'filetype <file>'",
              example=[
                  f"cli: -model_formal 'smv adder.smv'",
                  f"api: chip.set('model','formal','smv','adder.smv')"],
              schelp=f"""
              Filepaths to formal models specified on a per filetype basis.""")
  
      # rtl
      scparam(cfg,['model', 'rtl', filetype],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: RTL",
              switch=f"-model_rtl 'filetype <file>'",
              example=[
                  f"cli: -model_rtl 'systemc adder.sc'",
                  f"api: chip.set('model','rtl','systemc','adder.sc')"],
              schelp=f"""
              Filepaths to cycle accurate model specified on a per filetype
              basis.""")
  
      # IO
      scparam(cfg,['model', 'io', filetype],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: IO",
              switch=f"-model_io 'filetype <file>'",
              example=[
                  f"cli: -model_io 'ibis top.ibs'",
                  f"api: chip.set('model','io','ibis','top.ibis')"],
              schelp=f"""
              Filepaths to IO models specified on a per filetype basis.""")
  
      # thermal
      scparam(cfg,['model', 'thermal', filetype],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: Thermal",
              switch=f"-model_thermal 'filetype corner <file>'",
              example=[
                  f"cli: -model_thermal 'delphi adder_thermal.sp'",
                  f"api: chip.set('model','thermal','delphi','adder_thermal.sp')"],
              schelp=f"""
              Filepaths to thermal models.""")
  
      # timing
      scparam(cfg,['model', 'timing', filetype, corner],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: Timing",
              switch=f"-model_timing 'filetype corner <file>'",
              example=[
                  f"cli: -model_timing 'nldm-libgz ss ss.lib.gz'",
                  f"api: chip.set('model','timing','nldm-ldb','ss','ss.ldb')"],
              schelp=f"""
              Filepaths to static timing models specified on a per filetype and
              per corner basis.  Examples of filetypes include: nldm, nldm-ldb,
              nldm-libgz, ccs, ccs-libgz, ccs-ldb, scm, scm-libgz, scm-ldb,
              aocv, aocv-libgz, aocv-ldb.""")
  
      # power
      scparam(cfg,['model', 'power', filetype, corner],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: Power",
              switch=f"-model_power 'filetype corner <file>'",
              example=[
                  f"cli: -model_power 'apl ss adder.ss.apl'",
                  f"api: chip.set('model','power','apl','ss','adder.ss.apl')"],
              schelp=f"""
              Filepaths to power/current models.""")
  
      # layout
      scparam(cfg,['model', 'layout', filetype, stackup],
              sctype='[file]',
              scope='global',
              shorthelp=f"Model: Layout",
              switch=f"-model_layout 'filetype stackup <file>'",
              example=[
                  f"cli: -model_layout 'lef 10M adder.lef'",
                  f"api: chip.set('model','layout','lef','10M','adder.lef')"],
              schelp=f"""
              Filepaths to abstract layout views specified on a per filetype
              and per stackup basis.""")
  
      return cfg
  
  ###############################################################################
  # Datasheet
  ###############################################################################
  
  def schema_datasheet(cfg, design='default', name='default', mode='default'):
  
      # Device Features
      scparam(cfg, ['datasheet', design, 'feature', name],
              sctype='float',
              shorthelp=f"Datasheet: device feature specification",
              switch=f"-datasheet_feature 'design name <float>'",
              example=[
                  f"cli: -datasheet_feature 'mydevice ram 64e6'",
                  f"api: chip.set('datasheet','mydevice','feature','ram', 1e9)"],
              schelp=f"""Quantity of a specified feature. The 'unit'
              field should be used to specify the units used when unclear.""")
  
      # Absolute max voltage
      scparam(cfg, ['datasheet', design, 'limits', 'voltage', name],
              sctype='(float,float)',
              shorthelp=f"Datasheet: absolute voltage limits",
              switch=f"-datasheet_limits_voltage 'design pin <(float,float)>'",
              example=[
                  f"cli: -datasheet_limits_voltage 'mydevice vdd (-0.4,1.1)'",
                  f"api: chip.set('datasheet','mydevice','limits','voltage','vdd', (-0.4,1.1))"],
              schelp=f"""Device absolute minimum/maximum voltage not to be
              exceeded, specified on a per pin basis.""")
  
      # Absolute max temperatures
      metrics = {'storagetemp': 'storage temperature limits',
                 'junctiontemp' :'junction temperature limits'}
  
      for item, val in metrics.items():
          scparam(cfg, ['datasheet', design, 'limits', item],
                  sctype='(float,float)',
                  shorthelp=f"Datasheet: absolute {val}",
                  switch=f"-datasheet_{item} 'design <(float,float)>'",
                  example=[
                      f"cli: -datasheet_{item} 'mydevice (-40,125)'",
                      f"api: chip.set('datasheet','mydevice','limits','{item}',(-40,125))"],
                  schelp=f"""Device absolute {val} not to be exceeded.""")
  
      # Package Pin Map
      package = 'default'
      scparam(cfg, ['datasheet', design, 'pin', name, 'map', package],
              sctype='str',
              shorthelp=f"Datasheet: package pin map",
              switch=f"-datasheet_pin_map 'design name package <str>'",
              example=[
                  f"cli: -datasheet_pin_map 'mydevice in0 bga512 B4'",
                  f"api: chip.set('datasheet','mydevice','pin','in0','map','bga512','B4')"],
              schelp=f"""Signal to package pin mapping specified on a per package basis.""")
  
      # Pin type
      scparam(cfg, ['datasheet', design, 'pin', name, 'type', mode],
              sctype='str',
              shorthelp=f"Datasheet: pin type",
              switch=f"-datasheet_pin_type 'design name mode <str>'",
              example=[
                  f"cli: -datasheet_pin_type 'mydevice vdd type power'",
                  f"api: chip.set('datasheet','mydevice','pin','vdd','type','global','power')"],
              schelp=f"""Pin type specified on a per mode basis. Acceptable pin types
              include: digital, analog, clk, power, ground""")
  
      # Pin direction
      scparam(cfg, ['datasheet', design, 'pin', name, 'dir', mode],
              sctype='str',
              shorthelp=f"Datasheet: pin direction",
              switch=f"-datasheet_pin_dir 'design name mode <str>'",
              example=[
                  f"cli: -datasheet_pin_dir 'mydevice clk global input'",
                  f"api: chip.set('datasheet','mydevice','pin','clk','dir','global','input')"],
              schelp=f"""Pin direction specified on a per mode basis. Acceptable pin
              directions include: input, output, inout.""")
  
      # Complementary pin (for differential pair)
      scparam(cfg, ['datasheet', design, 'pin', name, 'complement', mode],
              sctype='str',
              shorthelp=f"Datasheet: pin complement",
              switch=f"-datasheet_pin_complement 'design name mode <str>'",
              example=[
                  f"cli: -datasheet_pin_complement 'mydevice ina global inb'",
                  f"api: chip.set('datasheet','mydevice','pin','ina','complement','global','inb')"],
              schelp=f"""Pin complement specified on a per mode basis for differential
              signals.""")
  
      # Related clock
      scparam(cfg, ['datasheet', design, 'pin', name, 'clk', mode],
                  sctype='str',
                  shorthelp=f"Datasheet: pin related clock",
                  switch=f"-datasheet_pin_clk 'design name mode <str>'",
                  example=[
                      f"cli: -datasheet_pin_clk 'mydevice ina global clka'",
                      f"api: chip.set('datasheet','mydevice','pin','ina','clk','global','clka')"],
              schelp=f"""Pin related clock specified on a per mode basis.""")
  
      # Related supply
      scparam(cfg, ['datasheet', design, 'pin', name, 'supply', mode],
                  sctype='str',
                  shorthelp=f"Datasheet: pin related power supply",
                  switch=f"-datasheet_pin_supply 'design name mode <str>'",
                  example=[
                      f"cli: -datasheet_pin_supply 'mydevice ina global vdd'",
                      f"api: chip.set('datasheet','mydevice','pin','ina','supply','global','vdd')"],
              schelp=f"""Pin related power supply specified on a per mode basis.""")
  
      # Related ground
      scparam(cfg, ['datasheet', design, 'pin', name, 'ground', mode],
                  sctype='str',
                  shorthelp=f"Datasheet: pin related ground",
                  switch=f"-datasheet_pin_ground 'design name mode <str>'",
                  example=[
                      f"cli: -datasheet_pin_ground 'mydevice ina ground vss'",
                      f"api: chip.set('datasheet','mydevice','pin','ina','ground','global','vss')"],
              schelp=f"""Pin related ground rail specified on a per mode basis.""")
  
      # Standard
      scparam(cfg, ['datasheet', design, 'pin', name, 'standard', mode],
              sctype='[str]',
              shorthelp=f"Datasheet: pin standard",
              switch=f"-datasheet_pin_standard 'design name mode <str>'",
              example=[
                  f"cli: -datasheet_pin_standard 'mydevice ba0 global ddr4'",
                  f"api: chip.set('datasheet','mydevice','pin','ina','standard','global','ddr4')"],
              schelp=f"""Pin communication standard specified on a per mode basis.""")
  
      # Reset value
      scparam(cfg, ['datasheet', design, 'pin', name, 'resetvalue', mode],
              sctype='[str]',
              shorthelp=f"Datasheet: pin reset value",
              switch=f"-datasheet_pin_resetvalue 'design name mode <str>'",
              example=[
                  f"cli: -datasheet_pin_resetvalue 'mydevice clk global weak1'",
                  f"api: chip.set('datasheet','mydevice','pin','clk','resetvalue','global','weak1')"],
              schelp=f"""Pin reset value specified on a per mode basis. Legal reset
              values include weak1, weak0, strong0, strong1, highz.""")
  
      # DC levels
      metrics = {'vol': ['low output voltage level', (-0.2,0,0.2), 'V'],
                 'voh': ['high output voltage level', (4.6,4.8,5.2), 'V'],
                 'vil': ['low input voltage level', (-0.2, 0, 1.0), 'V'],
                 'vih': ['high input voltage level', (1.4, 1.8, 2.2), 'V'],
                 'vcm': ['common mode voltage', (0.3, 1.2, 1.6), 'V'],
                 'vdiff': ['differential voltage', (0.2, 0.3, 0.9), 'V'],
                 'vnoise': ['random voltage noise', (0,0.01,0.1), 'V'],
                 'vhbm': ['machine model ESD tolerance', (100, 300, 500), 'V'],
                 'vcdm': ['human body model ESD tolerance', (100,300,500), 'V'],
                 'rdiff': ['differential pair resistance', (45,50,55), 'ohm'],
                 'rpullup': ['pullup resistance', (1000, 1200, 3000), 'ohm'],
                 'rpulldown': ['pulldown resistance', (1000, 1200, 3000), 'ohm'],
                 'idrive': ['drive current', (10e-3, 12e-3, 15e-3), 'A'],
                 'iinject': ['injection current', (1e-3, 1.2e-3, 1.5e-3), 'A'],
                 'ileakage': ['leakage current', (1e-6, 1.2e-6, 1.5e-6), 'A'],
                 'capacitance': ['capacitance', (1e-12, 1.2e-12, 1.5e-12), 'F']}
  
      for item, val in metrics.items():
          scparam(cfg, ['datasheet', design, 'pin', name, item, mode],
                  unit=val[2],
                  sctype='(float,float,float)',
                  shorthelp=f"Datasheet: {val[0]}",
                  switch=f"-datasheet_pin_{item} 'design pin mode <(float,float,float)>'",
                  example=[
                      f"cli: -datasheet_pin_{item} 'mydevice sclk global {val[1]}'",
                      f"api: chip.set('datasheet','mydevice','pin','sclk','{item}','global',{val[1]}"],
                  schelp=f"""Pin {val[0]}. Values are tuples of (min, typical, max).""")
  
      # AC Timing
      metrics = {'tsetup': ['setup time', (1e-9, 2e-9, 4e-9), 's'],
                 'thold': ['hold time', (1e-9, 2e-9, 4e-9), 's'],
                 'trise': ['rise transition', (1e-9, 2e-9, 4e-9), 's'],
                 'tfall': ['fall transition', (1e-9, 2e-9, 4e-9), 's'],
                 'tperiod': ['minimum period', (1e-9, 2e-9, 4e-9), 's'],
                 'tpulse': ['pulse width', (1e-9, 2e-9, 4e-9), 's'],
                 'tjitter': ['rms jitter', (1e-9, 2e-9, 4e-9), 's'],
                 'dutycycle': ['duty cycle', (45, 50, 55), '%']}
  
      for item, val in metrics.items():
          scparam(cfg, ['datasheet', design, 'pin', name, item, mode],
                  unit=val[2],
                  sctype='(float,float,float)',
                  shorthelp=f"Datasheet: {val[0]}",
                  switch=f"-datasheet_pin_{item} 'design pin mode <(float,float,float)>'",
                  example=[
                      f"cli: -datasheet_pin_{item} 'mydevice sclk global {val[1]}'",
                      f"api: chip.set('datasheet','mydevice','pin','sclk','{item}','global',{val[1]}"],
                  schelp=f"""Pin {val[0]}. Values are tuples of (min, typical, max).""")
  
      return cfg
  
  ###############################################################################
  # Flow Configuration
  ###############################################################################
  
  def schema_flowgraph(cfg, flow='default', step='default', index='default'):
  
      # flowgraph input
      scparam(cfg,['flowgraph', flow, step, index, 'input'],
              sctype='[(str,str)]',
              shorthelp="Flowgraph: step input",
              switch="-flowgraph_input 'flow step index <(str,str)>'",
              example=[
                  "cli: -flowgraph_input 'asicflow cts 0 (place,0)'",
                  "api:  chip.set('flowgraph','asicflow','cts','0','input',('place','0'))"],
              schelp="""A list of inputs for the current step and index, specified as a
              (step,index) tuple.""")
  
      # flowgraph metric weights
      metric='default'
      scparam(cfg,['flowgraph', flow, step, index, 'weight', metric],
              sctype='float',
              shorthelp="Flowgraph: metric weights",
              switch="-flowgraph_weight 'flow step index metric <float>'",
              example=[
                  "cli: -flowgraph_weight 'asicflow cts 0 area_cells 1.0'",
                  "api:  chip.set('flowgraph','asicflow','cts','0','weight','area_cells',1.0)"],
              schelp="""Weights specified on a per step and per metric basis used to give
              effective "goodnes" score for a step by calculating the sum all step
              real metrics results by the corresponding per step weights.""")
  
      scparam(cfg,['flowgraph', flow, step, index, 'goal', metric],
              sctype='float',
              shorthelp="Flowgraph: metric goals",
              switch="-flowgraph_goal 'flow step index metric <float>'",
              example=[
                  "cli: -flowgraph_goal 'asicflow cts 0 area_cells 1.0'",
                  "api:  chip.set('flowgraph','asicflow','cts','0','goal','errors', 0)"],
              schelp="""Goals specified on a per step and per metric basis used to
              determine whether a certain task can be considered when merging
              multiple tasks at a minimum or maximum node. A task is considered
              failing if the absolute value of any of its metrics are larger than
              the goal for that metric, if set.""")
  
      # flowgraph tool
      scparam(cfg,['flowgraph', flow, step, index, 'tool'],
              sctype='str',
              shorthelp="Flowgraph: tool selection",
              switch="-flowgraph_tool 'flow step <str>'",
              example=[
                  "cli: -flowgraph_tool 'asicflow place 0 openroad'",
                  "api: chip.set('flowgraph','asicflow','place','0','tool','openroad')"],
              schelp="""Name of the tool name used for task execution. Builtin tool names
              associated bound to core API functions include: minimum, maximum, join,
              verify, mux.""")
  
      # flowgraph arguments
      scparam(cfg,['flowgraph', flow, step, index, 'args'],
              sctype='[str]',
              shorthelp="Flowgraph: setup arguments",
              switch="-flowgraph_args 'flow step index <str>'",
              example=[
                  "cli: -flowgraph_args 'asicflow cts 0 0'",
                  "api:  chip.add('flowgraph','asicflow','cts','0','args','0')"],
              schelp="""User specified flowgraph string arguments specified on a per
              step and per index basis.""")
  
      # flowgraph valid bits
      scparam(cfg,['flowgraph', flow, step, index, 'valid'],
              sctype='bool',
              shorthelp="Flowgraph: task valid bit",
              switch="-flowgraph_valid 'flow step index <str>'",
              example=[
                  "cli: -flowgraph_valid 'asicflow cts 0 true'",
                  "api:  chip.set('flowgraph','asicflow','cts','0','valid',True)"],
              schelp="""Flowgraph valid bit specified on a per step and per index basis.
              The parameter can be used to control flow execution. If the bit
              is cleared (0), then the step/index combination is invalid and
              should not be run.""")
  
      # flowgraph timeout value
      scparam(cfg,['flowgraph', flow, step, index, 'timeout'],
              sctype='float',
              unit='s',
              shorthelp="Flowgraph: task timeout value",
              switch="-flowgraph_timeout 'flow step 0 <float>'",
              example=[
                  "cli: -flowgraph_timeout 'asicflow cts 0 3600'",
                  "api:  chip.set('flowgraph','asicflow','cts','0','timeout', 3600)"],
              schelp="""Timeout value in seconds specified on a per step and per index
              basis. The flowgraph timeout value is compared against the
              wall time tracked by the SC runtime to determine if an
              operation should continue. Timeout values help in situations
              where 1.) an operation is stuck and may never finish. 2.) the
              operation progress has saturated and continued execution has
              a negative return on investment.""")
  
      # flowgraph status
      scparam(cfg,['flowgraph', flow, step, index, 'status'],
              sctype='str',
              shorthelp="Flowgraph: task status",
              switch="-flowgraph_status 'flow step index <str>'",
              example=[
                  "cli: -flowgraph_status 'asicflow cts 10 success'",
                  "api:  chip.set('flowgraph','asicflow', 'cts','10','status', 'success')"],
              schelp="""Parameter that tracks the status of a task. Valid values are:
  
              * "success": task ran successfully
              * "error": task failed with an error
  
              An empty value indicates the task has not yet been completed.""")
  
      # flowgraph select
      scparam(cfg,['flowgraph', flow, step, index, 'select'],
              sctype='[(str,str)]',
              shorthelp="Flowgraph: task select record",
              switch="-flowgraph_select 'flow step index <(str,str)>'",
              example= [
                  "cli: -flowgraph_select 'asicflow cts 0 (place,42)'",
                  "api:  chip.set('flowgraph','asicflow', 'cts','0','select',('place','42'))"],
              schelp="""
              List of selected inputs for the current step/index specified as
              (in_step,in_index) tuple.""")
  
      return cfg
  
  
  ###########################################################################
  # Tool Setup
  ###########################################################################
  
  def schema_tool(cfg, tool='default', step='default', index='default'):
  
      key = 'default'
      suffix = 'default'
      metric = 'default'
  
      scparam(cfg, ['tool', tool, 'exe'],
              sctype='str',
              shorthelp="Tool: executable name",
              switch="-tool_exe 'tool <str>'",
              example=["cli: -tool_exe 'openroad openroad'",
                       "api:  chip.set('tool','openroad','exe','openroad')"],
              schelp="""Tool executable name.""")
  
      scparam(cfg, ['tool', tool, 'path'],
              sctype='dir',
              shorthelp="Tool: executable path",
              switch="-tool_path 'tool <dir>'",
              example=["cli: -tool_path 'openroad /usr/local/bin'",
                       "api:  chip.set('tool','openroad','path','/usr/local/bin')"],
              schelp="""
              File system path to tool executable. The path is prepended to the
              system PATH environment variable for batch and interactive runs. The
              path parameter can be left blank if the 'exe' is already in the
              environment search path.""")
  
      scparam(cfg, ['tool', tool, 'vswitch'],
              sctype='[str]',
              shorthelp="Tool: executable version switch",
              switch="-tool_vswitch 'tool <str>'",
              example=["cli: -tool_vswitch 'openroad -version'",
                       "api:  chip.set('tool','openroad','vswitch','-version')"],
              schelp="""
              Command line switch to use with executable used to print out
              the version number. Common switches include -v, -version,
              --version. Some tools may require extra flags to run in batch mode.""")
  
      scparam(cfg, ['tool', tool, 'vendor'],
              sctype='str',
              shorthelp="Tool: vendor",
              switch="-tool_vendor 'tool <str>'",
              example=["cli: -tool_vendor 'yosys yosys'",
                       "api: chip.set('tool','yosys','vendor','yosys')"],
              schelp="""
              Name of the tool vendor. Parameter can be used to set vendor
              specific technology variables in the PDK and libraries. For
              open source projects, the project name should be used in
              place of vendor.""")
  
      scparam(cfg, ['tool', tool, 'version'],
              sctype='[str]',
              shorthelp="Tool: version",
              switch="-tool_version 'tool <str>'",
              example=["cli: -tool_version 'openroad >=v2.0'",
                       "api:  chip.set('tool','openroad','version','>=v2.0')"],
              schelp="""
              List of acceptable versions of the tool executable to be used. Each
              entry in this list must be a version specifier as described by Python
              `PEP-440 <https://peps.python.org/pep-0440/#version-specifiers>`_.
              During task execution, the tool is called with the 'vswitch' to
              check the runtime executable version. If the version of the system
              executable is not allowed by any of the specifiers in 'version',
              then the job is halted pre-execution. For backwards compatibility,
              entries that do not conform to the standard will be interpreted as a
              version with an '==' specifier. This check can be disabled by
              setting 'novercheck' to True.""")
  
      scparam(cfg, ['tool', tool, 'format'],
              sctype='str',
              shorthelp="Tool: manifest file format",
              switch="-tool_format 'tool <file>'",
              example=[ "cli: -tool_format 'yosys tcl'",
                        "api: chip.set('tool','yosys','format','tcl')"],
              schelp="""
              File format for tool manifest handoff. Supported formats are tcl,
              yaml, and json.""")
  
      scparam(cfg, ['tool', tool, 'warningoff'],
              sctype='[str]',
              shorthelp="Tool: warning filter",
              switch="-tool_warningoff 'tool <str>'",
              example=["cli: -tool_warningoff 'verilator COMBDLY'",
                       "api: chip.set('tool','verilator','warningoff','COMBDLY')"],
              schelp="""
              A list of EDA warnings for which printing should be suppressed.
              Generally this is done on a per design basis after review has
              determined that warning can be safely ignored The code for turning
              off warnings can be found in the specific tool reference manual.
              """)
  
      scparam(cfg, ['tool', tool, 'continue'],
              sctype='bool',
              shorthelp="Tool: continue-on-error option",
              switch="-tool_continue 'tool <bool>'",
              example=["cli: -tool_continue 'verilator true'",
                       "api: chip.set('tool','verilator','continue', true)"],
              schelp="""
              Directs tool to continue operating even if errors are
              encountered.""")
  
      scparam(cfg, ['tool', tool, 'licenseserver', key],
              sctype='[str]',
              shorthelp="Tool: license servers",
              switch="-tool_licenseserver 'name key <str>'",
              example=[
                  "cli: -tool_licenseserver 'atool ACME_LICENSE 1700@server'",
                  "api: chip.set('tool','atool','licenseserver','ACME_LICENSE','1700@server')"],
              schelp="""
              Defines a set of tool specific environment variables used by the executables
              that depend on license key servers to control access. For multiple servers,
              separate each server by a 'colon'. The named license variable are read at
              runtime (run()) and the environment variables are set.
              """)
  
      #######################
      # Per step/index
      ########################
  
      scparam(cfg, ['tool', tool, 'regex', step, index, suffix],
              sctype='[str]',
              shorthelp="Tool: regex filter",
              switch="-tool_regex 'tool step index suffix <str>'",
              example=[
                  "cli: -tool_regex 'openroad place 0 error -v ERROR'",
                  "api: chip.set('tool','openroad','regex','place','0','error','-v ERROR')"],
              schelp="""
              A list of piped together grep commands. Each entry represents a set
              of command line arguments for grep including the regex pattern to
              match. Starting with the first list entry, each grep output is piped
              into the following grep command in the list. Supported grep options
              include ``-v`` and ``-e``. Patterns starting with "-" should be
              directly preceeded by the ``-e`` option. The following example
              illustrates the concept.
  
              UNIX grep:
  
              .. code-block:: bash
  
                  $ grep WARNING place.log | grep -v "bbox" > place.warnings
  
              SiliconCompiler::
  
                  chip.set('tool', 'openroad', 'regex', 'place', '0', 'warnings', ["WARNING", "-v bbox"])
              """)
  
  
      scparam(cfg, ['tool', tool, 'option', step, index],
              sctype='[str]',
              shorthelp="Tool: executable options",
              switch="-tool_option 'tool step index <str>'",
              example=[
                  "cli: -tool_option 'openroad cts 0 -no_init'",
                  "api: chip.set('tool','openroad','option','cts','0','-no_init')"],
              schelp="""
              List of command line options for the tool executable, specified on
              a per tool and per step basis. Options must not include spaces.
              For multiple argument options, each option is a separate list element.
              """)
  
      scparam(cfg, ['tool', tool, 'var', step, index, key],
              sctype='[str]',
              shorthelp="Tool: script variables",
              switch="-tool_variable 'tool step index key <str>'",
              example=[
                  "cli: -tool_variable 'openroad cts 0 myvar 42'",
                  "api: chip.set('tool','openroad','var','cts','0','myvar','42')"],
              schelp="""
              Tool script variables specified as key value pairs. Variable
              names and value types must match the name and type of tool and reference
              script consuming the variable.""")
  
      scparam(cfg, ['tool', tool, 'env', step, index, key],
              sctype='str',
              shorthelp="Tool: environment variables",
              switch="-tool_env 'tool step index name <str>'",
              example=[
                  "cli: -tool_env 'openroad cts 0 MYVAR 42'",
                  "api: chip.set('tool','openroad','env','cts','0','MYVAR','42')"],
              schelp="""
              Environment variables to set for individual tasks. Keys and values
              should be set in accordance with the tool's documentation. Most
              tools do not require extra environment variables to function.""")
  
      scparam(cfg, ['tool', tool, 'input', step, index],
              sctype='[file]',
              shorthelp="Tool: input files",
              switch="-tool_input 'tool step index <str>'",
              example=[
                  "cli: -tool_input 'openroad place 0 oh_add.def'",
                  "api: chip.set('tool','openroad','input','place','0','oh_add.def')"],
              schelp="""
              List of data files to be copied from previous flowgraph steps 'output'
              directory. The list of steps to copy files from is defined by the
              list defined by the dictionary key ['flowgraph', step, index, 'input'].
              All files must be available for flow to continue. If a file
              is missing, the program exists on an error.""")
  
      scparam(cfg, ['tool', tool, 'output', step, index],
              sctype='[file]',
              shorthelp="Tool: output files",
              switch="-tool_output 'tool step index <str>'",
              example=[
                  "cli: -tool_output 'openroad place 0 oh_add.def'",
                  "api: chip.set('tool','openroad','output','place','0','oh_add.def')"],
              schelp="""
              List of data files to be copied from previous flowgraph steps 'output'
              directory. The list of steps to copy files from is defined by the
              list defined by the dictionary key ['flowgraph', step, index, 'output'].
              All files must be available for flow to continue. If a file
              is missing, the program exists on an error.""")
  
      scparam(cfg, ['tool', tool, 'stdout', step, index, 'destination'],
              sctype='str',
              defvalue='log',
              scope='job',
              shorthelp="Tool: Destination for stdout",
              switch="-tool_stdout_destination 'tool step index [log|output|none]'",
              example=["cli: -tool_stdout_destination 'ghdl import 0 log'",
                      "api: chip.set('tool','ghdl','stdout','import','0','destination','log')"],
              schelp="""
              Defines where to direct the output generated over stdout.
              Supported options are:
              none: the stream generated to STDOUT is ignored
              log: the generated stream is stored in <step>.<suffix>; if not in quiet mode,
              it is additionally dumped to the display output: the generated stream is stored
              in outputs/<design>.<suffix>""")
  
      scparam(cfg, ['tool', tool, 'stdout', step, index, 'suffix'],
              sctype='str',
              defvalue='log',
              scope='job',
              shorthelp="Tool: File suffix for redirected stdout",
              switch="-tool_stdout_suffix 'tool step index <str>'",
              example=["cli: -tool_stdout_suffix 'ghdl import 0 log'",
                      "api: chip.set('tool','ghdl','stdout','import','0','suffix','log')"],
              schelp="""
              Specifies the file extension for the content redirected from stdout.""")
  
      scparam(cfg, ['tool', tool, 'stderr', step, index, 'destination'],
              sctype='str',
              defvalue='log',
              scope='job',
              shorthelp="Tool: Destination for stderr",
              switch="-tool_stderr_destination 'tool step index [log|output|none]'",
              example=["cli: -tool_stderr_destination 'ghdl import 0 log'",
                      "api: chip.set('tool','ghdl','stderr','import','0','destination','log')"],
              schelp="""
              Defines where to direct the output generated over stderr.
              Supported options are:
              none: the stream generated to STDERR is ignored
              log: the generated stream is stored in <step>.<suffix>; if not in quiet mode,
              it is additionally dumped to the display output: the generated stream is
              stored in outputs/<design>.<suffix>""")
  
      scparam(cfg, ['tool', tool, 'stderr', step, index, 'suffix'],
              sctype='str',
              defvalue='log',
              scope='job',
              shorthelp="Tool: File suffix for redirected stderr",
              switch="-tool_stderr_suffix 'tool step index <str>'",
              example=["cli: -tool_stderr_suffix 'ghdl import 0 log'",
                      "api: chip.set('tool','ghdl','stderr','import','0','suffix','log')"],
              schelp="""
              Specifies the file extension for the content redirected from stderr.""")
  
      scparam(cfg, ['tool', tool, 'require', step, index],
              sctype='[str]',
              shorthelp="Tool: parameter requirements",
              switch="-tool_require 'tool step index <str>'",
              example=[
                  "cli: -tool_require 'openroad cts 0 design'",
                  "api: chip.set('tool','openroad','require','cts','0','design')"],
              schelp="""
              List of keypaths to required tool parameters. The list is used
              by check() to verify that all parameters have been set up before
              step execution begins.""")
  
      scparam(cfg, ['tool', tool, 'report', step, index, metric],
              sctype='[file]',
              shorthelp="Tool: report files",
              switch="-tool_report 'tool step index metric <str>'",
              example=[
                   "cli: -tool_report 'openroad place 0 holdtns place.log'",
                  "api: chip.set('tool','openroad','report','syn','0','holdtns','place.log')"],
              schelp="""
              List of report files associated with a specific 'metric'. The file path
              specified is relative to the run directory of the current task.""")
  
      scparam(cfg, ['tool', tool, 'refdir', step, index],
              sctype='[dir]',
              shorthelp="Tool: script directory",
              switch="-tool_refdir 'tool step index <dir>'",
              example=[
                  "cli: -tool_refdir 'yosys syn 0 ./myref'",
                  "api:  chip.set('tool','yosys','refdir','syn','0','./myref')"],
              schelp="""
              Path to directories containing reference flow scripts, specified
              on a per step and index basis.""")
  
      scparam(cfg, ['tool', tool, 'script', step, index],
              sctype='[file]',
              shorthelp="Tool: entry script",
              switch="-tool_script 'tool step index <file>'",
              example=[
                  "cli: -tool_script 'yosys syn 0 syn.tcl'",
                  "api: chip.set('tool','yosys','script','syn','0','syn.tcl')"],
              schelp="""
              Path to the entry script called by the executable specified
              on a per tool and per step basis.""")
  
      scparam(cfg, ['tool', tool, 'keep', step, index],
              sctype='[str]',
              shorthelp="Tool: files to keep",
              switch="-tool_keep 'tool step index <str>'",
              example=[
                  "cli: -tool_keep 'surelog import 0 slp_all'",
                  "api: chip.set('tool','surelog','script','import','0','slpp_all')"],
              schelp="""
              Names of additional files and directories in the work directory that
              should be kept when :keypath:`option, clean` is true.""")
  
      scparam(cfg, ['tool', tool, 'threads', step, index],
              sctype='int',
              shorthelp="Tool: thread parallelism",
              switch="-tool_threads 'tool step index <int>'",
              example=["cli: -tool_threads 'magic drc 0 64'",
                       "api: chip.set('tool','magic','threads','drc','0','64')"],
              schelp="""
              Thread parallelism to use for execution specified on a per tool and per
              step basis. If not specified, SC queries the operating system and sets
              the threads based on the maximum thread count supported by the
              hardware.""")
  
      return cfg
  
  ###########################################################################
  #  Function arguments
  ###########################################################################
  
  def schema_arg(cfg):
  
      key = 'default'
      scparam(cfg, ['arg', 'pdk', key],
              sctype='[str]',
              scope='scratch',
              shorthelp="ARG: PDK argument",
              switch="-arg_pdk 'key <str>",
              example=[
                  "cli: -arg_pdk 'mimcap true'",
                  "api: chip.set('arg','pdk','mimcap','true')"],
              schelp="""
              Parameter passed in as key/value pair to the technology target
              referenced in the load_pdk() API call. See the target technology
              for specific guidelines regarding configuration parameters.""")
  
      scparam(cfg, ['arg', 'flow', key],
              sctype='[str]',
              scope='scratch',
              shorthelp="ARG: Flow argument",
              switch="-arg_flow 'key <str>'",
              example=[
                  "cli: -arg_flow 'n 100'",
                  "api: chip.set('arg','flow','n', 100)"],
              schelp="""
              Parameter passed in as key/value pair to the flow target
              referenced in the load_flow() API call. See the target flow
              for specific guidelines regarding configuration parameters.""")
  
      scparam(cfg, ['arg', 'step'],
              sctype='str',
              scope='scratch',
              shorthelp="ARG: Step argument",
              switch="-arg_step <str>",
              example=["cli: -arg_step 'route'",
                      "api: chip.set('arg', 'step', 'route')"],
              schelp="""
              Dynamic parameter passed in by the sc runtime as an argument to
              a runtime task. The parameter enables configuration code
              (usually TCL) to use control flow that depend on the current
              'step'. The parameter is used the run() function and
              is not intended for external use.""")
  
      scparam(cfg, ['arg', 'index'],
              sctype='str',
              scope='scratch',
              shorthelp="ARG: Index argument",
              switch="-arg_index <str>",
              example=["cli: -arg_index 0",
                      "api: chip.set('arg','index','0')"],
              schelp="""
              Dynamic parameter passed in by the sc runtime as an argument to
              a runtime task. The parameter enables configuration code
              (usually TCL) to use control flow that depend on the current
              'index'. The parameter is used the run() function and
              is not intended for external use.""")
  
      return cfg
  
  ###########################################################################
  # Metrics to Track
  ###########################################################################
  
  def schema_metric(cfg, step='default', index='default'):
  
      metrics = {'errors': 'errors',
                 'warnings' :'warnings',
                 'drvs' : 'design rule violations',
                 'unconstrained' : 'unconstrained timing paths'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='int',
                  shorthelp=f"Metric: total {item}",
                  switch=f"-metric_{item} 'step index <int>'",
                  example=[
                      f"cli: -metric_{item} 'dfm 0 0'",
                      f"api: chip.set('metric','dfm','0','{item}', 0)"],
                  schelp=f"""Metric tracking the total number of {val} on a
                  per step and index basis.""")
  
      scparam(cfg, ['metric', step, index, 'coverage'],
              sctype='float',
              unit='%',
              shorthelp=f"Metric: coverage",
              switch="-metric_coverage 'step index <float>'",
              example=[
                  "cli: -metric_coverage 'place 0 99.9'",
                  "api: chip.set('metric','place','0','coverage', 99.9)"],
              schelp=f"""
              Metric tracking the test coverage in the design expressed as a percentage
              with 100 meaning full coverage. The meaning of the metric depends on the
              task being executed. It can refer to code coverage, feature coverage,
              stuck at fault coverage.""")
  
      scparam(cfg, ['metric', step, index, 'security'],
              sctype='float',
              unit='%',
              shorthelp="Metric: security",
              switch="-metric_security 'step index <float>'",
              example=[
                  "cli: -metric_security 'place 0 100'",
                  "api: chip.set('metric','place','0','security', 100)"],
              schelp=f"""
              Metric tracking the level of security (1/vulnerability) of the design.
              A completely secure design would have a score of 100. There is no
              absolute scale for the security metrics (like with power, area, etc)
              so the metric will be task and tool dependent.""")
  
      metrics = {'luts': 'FPGA LUTs',
                 'dsps' :'FPGA DSP slices',
                 'brams' : 'FPGA BRAM tiles'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='int',
  
                  shorthelp=f"Metric: {val}",
                  switch=f"-metric_{item} 'step index <int>'",
                  example=[
                      f"cli: -metric_{item} 'place 0 100'",
                      f"api: chip.set('metric','place','0','{item}', 100)"],
                  schelp=f"""
                  Metric tracking the total {val} used by the design as reported
                  by the implementation tool. There is no standardized definition
                  for this metric across vendors, so metric comparisons can
                  generally only be done between runs on identical tools and
                  device families.""")
  
      metrics = {'cellarea': 'cell area (ignoring fillers)',
                 'totalarea' :'physical die area'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='float',
                  unit='um^2',
                  shorthelp=f"Metric: {item}",
                  switch=f"-metric_{item} 'step index <float>'",
                  example=[
                      f"cli: -metric_{item} 'place 0 100.00'",
                      f"api: chip.set('metric','place','0','{item}', 100.00)"],
                  schelp=f"""
                  Metric tracking the total {val} occupied by the design.""")
  
      scparam(cfg, ['metric', step, index, 'utilization'],
              sctype='float',
              unit='%',
              shorthelp=f"Metric: area utilization",
              switch=f"-metric_utilization step index <float>",
              example=[
                  f"cli: -metric_utilization 'place 0 50.00'",
                  f"api: chip.set('metric','place','0','utilization', 50.00)"],
              schelp=f"""
              Metric tracking the area utilization of the design calculated as
              100 * (cellarea/totalarea).""")
  
      metrics = {'peakpower': 'worst case total peak power',
                 'averagepower': 'average workload power',
                 'dozepower': 'power consumed while in low frequency operating mode',
                 'idlepower': 'power while not performing useful work',
                 'leakagepower' :'leakage power with rails active but without any dynamic switching activity',
                 'sleeppower': 'power consumed with some or all power rails gated off'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='float',
                  unit='mw',
                  shorthelp=f"Metric: {item}",
                  switch=f"-metric_{item} 'step index <float>'",
                  example=[
                      f"cli: -metric_{item} 'place 0 0.01'",
                      f"api: chip.set('metric','place','0','{item}', 0.01)"],
                  schelp=f"""
                  Metric tracking the {val} of the design specified on a per step
                  and index basis. Power metric depend heavily on the method
                  being used for extraction: dynamic vs static, workload
                  specification (vcd vs saif), power models, process/voltage/temperature.
                  The power {item} metric tries to capture the data that would
                  usually be reflected inside a datasheet given the approprate
                  footnote conditions.""")
  
      scparam(cfg, ['metric', step, index, 'irdrop'],
              sctype='float',
              unit='mv',
              shorthelp=f"Metric: peak IR drop",
              switch="-metric_irdrop 'step index <float>'",
              example=[
                  f"cli: -metric_irdrop 'place 0 0.05'",
                  f"api: chip.set('metric','place','0','irdrop', 0.05)"],
              schelp=f"""
              Metric tracking the peak IR drop in the design based on extracted
              power and ground rail parasitics, library power models, and
              switching activity. The switching activity calculated on a per
              node basis is taken from one of three possible sources, in order
              of priority: VCD file, SAIF file, 'activityfactor' parameter.""")
  
      metrics = {'holdpaths': 'hold',
                 'setuppaths': 'setup'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='int',
                  shorthelp=f"Metric: {item}",
                  switch=f"-metric_{item} 'step index <float>'",
                  example=[
                      f"cli: -metric_{item} 'place 0 10'",
                      f"api: chip.set('metric','place','0','{item}', 10)"],
                  schelp=f"""
                  Metric tracking the total number of timing paths violating {val}
                  constraints.""")
  
      metrics = {'holdslack': 'worst hold slack (positive or negative)',
                 'holdwns': 'worst negative hold slack (positive values truncated to zero)',
                 'holdtns': 'total negative hold slack (TNS)',
                 'setupslack': 'worst setup slack (positive or negative)',
                 'setupwns': 'worst negative setup slack (positive values truncated to zero)',
                 'setuptns': 'total negative setup slack (TNS)'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='float',
                  unit='ns',
                  shorthelp=f"Metric: {item}",
                  switch=f"-metric_{item} 'step index <float>'",
                  example=[
                      f"cli: -metric_{item} 'place 0 0.01'",
                      f"api: chip.set('metric','place','0','{item}', 0.01)"],
                  schelp=f"""
                  Metric tracking the {val} on a per step and index basis.""")
  
      metrics = {'macros': 'macros',
                 'cells': 'cell instances',
                 'registers': 'register instances',
                 'buffers': 'buffer and inverter instances',
                 'transistors': 'transistors',
                 'pins': 'pins',
                 'nets': 'nets',
                 'vias': 'vias'}
  
      for item, val in metrics.items():
          scparam(cfg, ['metric', step, index, item],
                  sctype='int',
                  shorthelp=f"Metric: {item}",
                  switch=f"-metric_{item} 'step index <float>'",
                  example=[
                      f"cli: -metric_{item} 'place 0 100'",
                      f"api: chip.set('metric','place','0','{item}', 50)"],
                  schelp=f"""
                  Metric tracking the total number of {val} in the design
                  on a per step and index basis.""")
  
      item = 'wirelength'
      scparam(cfg, ['metric', step, index, item],
              sctype='float',
              unit='um',
              shorthelp=f"Metric: {item}",
              switch=f"-metric_{item} 'step index <float>'",
              example=[
                  f"cli: -metric_{item} 'place 0 100.0'",
                  f"api: chip.set('metric','place','0','{item}', 50.0)"],
              schelp=f"""
              Metric tracking the total {item} of the design on a per step
              and index basis.""")
  
      item = 'overflow'
      scparam(cfg, ['metric', step, index, item],
              sctype='int',
              shorthelp=f"Metric: {item}",
              switch=f"-metric_{item} 'step index <float>'",
              example=[
                  f"cli: -metric_{item} 'place 0 0'",
                  f"api: chip.set('metric','place','0','{item}', 50)"],
              schelp=f"""
              Metric tracking the total number of overflow tracks for the routing
              on per step and index basis. Any non-zero number suggests an over
              congested design. To analyze where the congestion is occurring
              inspect the router log files for detailed per metal overflow
              reporting and open up the design to find routing hotspots.""")
  
      item = 'memory'
      scparam(cfg, ['metric', step, index, item],
              sctype='float',
              unit='B',
              scope='job',
              shorthelp=f"Metric: {item}",
              switch=f"-metric_{item} 'step index <float>'",
              example=[
                  f"cli: -metric_{item} 'dfm 0 10e9'",
                  f"api: chip.set('metric','dfm','0','{item}', 10e9)"],
              schelp=f"""
              Metric tracking total peak program memory footprint on a per
              step and index basis.""")
  
      item = 'exetime'
      scparam(cfg, ['metric', step, index, item],
              sctype='float',
              unit='s',
              shorthelp=f"Metric: {item}",
              switch=f"-metric_{item} 'step index <float>'",
              example=[
                  f"cli: -metric_{item} 'dfm 0 10.0'",
                  f"api: chip.set('metric','dfm','0','{item}', 10.0)"],
              schelp=f"""
              Metric tracking time spent by the eda executable 'exe' on a
              per step and index basis. It does not include the siliconcompiler
              runtime overhead or time waitig for I/O operations and
              inter-processor communication to complete.""")
  
      item = 'tasktime'
      scparam(cfg, ['metric', step, index, item],
              sctype='float',
              unit='s',
              shorthelp=f"Metric: {item}",
              switch=f"-metric_{item} 'step index <float>'",
              example=[
                  f"cli: -metric_{item} 'dfm 0 10.0'",
                  f"api: chip.set('metric','dfm','0','{item}', 10.0)"],
              schelp=f"""
              Metric trakcing the total amount of time spent on a task from
              beginning to end, including data transfers and pre/post
              processing.""")
  
      item = 'totaltime'
      scparam(cfg, ['metric', step, index, item],
              sctype='float',
              unit='s',
              shorthelp=f"Metric: {item}",
              switch=f"-metric_{item} 'step index <float>'",
              example=[
                  f"cli: -metric_{item} 'dfm 0 10.0'",
                  f"api: chip.set('metric','dfm','0','{item}', 10.0)"],
              schelp=f"""
              Metric tracking the total amount of time spent from the beginning
              of the run up to and including the current step and index.""")
  
      return cfg
  
  ###########################################################################
  # Design Tracking
  ###########################################################################
  
  def schema_record(cfg, step='default', index='default'):
  
      # setting up local data structure
      # <key>  : ['short help', 'example' 'extended help']
  
      records = {'userid': ['userid',
                            'wiley',
                            ''],
                 'publickey' : ['public key',
                                '<key>',
                                ''],
                 'machine' : ['machine name',
                              'carbon',
                              '(myhost, localhost, ...'],
                 'macaddr' : ['MAC address',
                              '<addr>',
                              ''],
                 'ipaddr' : ['IP address',
                             '<addr>',
                             ''],
                 'platform' : ['platform name',
                               'linux',
                               '(linux, windows, freebsd)'],
                 'distro' : ['distro name',
                             'ubuntu',
                             '(ubuntu, redhat, centos)'],
                 'arch' : ['hardware architecture',
                           'x86_64',
                           '(x86_64, rv64imafdc)'],
                 'starttime' : ['start time',
                                '2021-09-06 12:20:20',
                                'Time is reported in the ISO 8601 format YYYY-MM-DD HR:MIN:SEC'],
                 'endtime' : ['end time',
                              '2021-09-06 12:20:20',
                              'Time is reported in the ISO 8601 format YYYY-MM-DD HR:MIN:SEC'],
                 'region' : ['cloud region',
                             'US Gov Boston',
                             """Recommended naming methodology:
  
                             * local: node is the local machine
                             * onprem: node in on-premises IT infrastructure
                             * public: generic public cloud
                             * govcloud: generic US government cloud
                             * <region>: cloud and entity specific region string name
                             """],
                 'scversion': ['software version',
                               '1.0',
                               """Version number for the SiliconCompiler software."""],
                 'toolversion': ['tool version',
                                 '1.0',
                                 """The tool version captured correspnds to the 'tool'
                                 parameter within the 'eda' dictionary."""],
                 'toolpath': ['tool path',
                               '/usr/bin/openroad',
                               """Full path to tool executable used to run this
                               task."""],
                 'toolargs': ['tool CLI arguments',
                              '-I include/ foo.v',
                              'Arguments passed to tool via CLI.'],
                 'osversion': ['O/S version',
                               '20.04.1-Ubuntu',
                               """Since there is not standard version system for operating
                               systems, extracting information from is platform dependent.
                               For Linux based operating systems, the 'osversion' is the
                               version of the distro."""],
                 'kernelversion' : ['O/S kernel version',
                                    '5.11.0-34-generic',
                                    """Used for platforms that support a distinction
                                    between os kernels and os distributions."""]
      }
  
      for item,val in records.items():
          helpext = utils.trim(val[2])
          scparam(cfg, ['record', step, index, item],
                  sctype='str',
                  shorthelp=f"Record: {val[0]}",
                  switch=f"-record_{item} 'step index <str>'",
                  example=[
                      f"cli: -record_{item} 'dfm 0 <{val[1]}>'",
                      f"api: chip.set('record','dfm','0','{item}', <{val[1]}>)"],
                  schelp=f'Record tracking the {val[0]} per step and index basis. {helpext}')
  
      return cfg
  
  ###########################################################################
  # Global units
  ###########################################################################
  
  def schema_unit(cfg):
      '''
  
      '''
  
      units = {
          'time' : 'ns',
          'length' : 'um',
          'mass' : 'g',
          'capacitance' : 'pf',
          'resistance' : 'ohm',
          'inductance' : 'nh',
          'voltage' : 'mv',
          'current' : 'ma',
          'power' : 'mw',
          'energy' : 'pj'
      }
  
      for item,val in units.items():
          scparam(cfg, ['unit', item],
                  sctype='str',
                  defvalue=val,
                  shorthelp=f"Unit: {item}",
                  switch=f"-unit_{item} '<str>'",
                  example=[
                      f"cli: -unit_{item} '{val}'",
                      f"api: chip.set('unit','{item}',{val})"],
                  schelp=f"""
                  Units used for {item} when not explicitly specified.""")
  
      return cfg
  
  ###########################################################################
  # Run Options
  ###########################################################################
  
  def schema_option(cfg):
      ''' Technology agnostic run time options
      '''
  
      scparam(cfg, ['option', 'remote'],
              sctype='bool',
              scope='job',
              shorthelp="Enable remote processing",
              switch="-remote <bool>",
              example=[
                  "cli: -remote",
                  "api: chip.set('option','remote', True)"],
              schelp="""
              Sends job for remote processing if set to true. The remote
              option requires a credentials file to be placed in the home
              directory. Fore more information, see the credentials
              parameter.""")
  
      scparam(cfg, ['option', 'credentials'],
              sctype='[file]',
              scope='job',
              shorthelp="User credentials file",
              switch="-credentials <file>'",
              example=[
                  "cli: -credentials /home/user/.sc/credentials",
                  "api: chip.set('option', 'credentials','/home/user/.sc/credentials')"],
              schelp="""
              Filepath to credentials used for remote processing. If the
              credentials parameter is empty, the remote processing client program
              tries to access the ".sc/credentials" file in the user's home
              directory. The file supports the following fields:
  
              userid=<user id>
              secret_key=<secret key used for authentication>
              server=<ipaddr or url>""")
  
      scparam(cfg, ['option', 'jobscheduler'],
              sctype='str',
              scope='job',
              shorthelp="Job scheduler name",
              switch="-jobscheduler <str>",
              example=[
                  "cli: -jobscheduler slurm",
                  "api: chip.set('option','jobscheduler','slurm')"],
              schelp="""
              Sets the type of job scheduler to be used for each individual
              flowgraph steps. If the parameter is undefined, the steps are executed
              on the same machine that the SC was launched on. If 'slurm' is used,
              the host running the 'sc' command must be running a 'slurmctld' daemon
              managing a Slurm cluster. Additionally, the build directory ('-dir')
              must be located in shared storage which can be accessed by all hosts
              in the cluster.""")
  
      # Compilation
      scparam(cfg, ['option', 'mode'],
              sctype='str',
              scope='job',
              shorthelp="Compilation mode",
              switch="-mode <str>",
              example=[
              "cli: -mode asic",
              "api: chip.set('option','mode','asic')"],
              schelp="""
              Sets the operating mode of the compiler. Valid modes are:
              asic: RTL to GDS ASIC compilation
              fpga: RTL to bitstream FPGA compilation
              sim: simulation to verify design and compilation
              """)
  
      scparam(cfg, ['option','target'],
              sctype='str',
              scope='job',
              shorthelp="Compilation target",
              switch="-target <str>",
              example=["cli: -target freepdk45_demo",
                       "api: chip.set('option','target','freepdk45_demo')"],
              schelp="""
              Sets a target module to be used for compilation. The target
              module must set up all paramaters needed. The target module
              may load multiple flows and libraries.
              """)
  
      scparam(cfg, ['option','flow'],
              sctype='str',
              scope='job',
              shorthelp="Flow target",
              switch="-flow <str>",
              example=["cli: -flow asicfow",
                       "api: chip.set('option','flow','asicflow')"],
              schelp="""
              Sets the flow for the current run. The flow name
              must match up with a 'flow' in the flowgraph""")
  
      scparam(cfg, ['option','pdk'],
              sctype='str',
              scope='job',
              shorthelp="PDK target",
              switch="-pdk <str>",
              example=["cli: -pdk asap7",
                       "api: chip.set('option','pdk','asap7')"],
              schelp="""
              Sets the pdk for the current run. The pdk name
              must match up with a 'pdk' loaded with load_pdk.""")
  
      scparam(cfg, ['option','optmode'],
              sctype='str',
              scope='job',
              require='all',
              defvalue='O0',
              shorthelp="Optimization mode",
              switch="-O<str>",
              example=["cli: -O3",
                      "api: chip.set('option','optmode','O3')"],
              schelp="""
              The compiler has modes to prioritize run time and ppa. Modes
              include.
  
              (O0) = Exploration mode for debugging setup
              (O1) = Higher effort and better PPA than O0
              (O2) = Higher effort and better PPA than O1
              (O3) = Signoff quality. Better PPA and higher run times than O2
              (O4-O98) = Reserved (compiler/target dependent)
              (O99) = Experimental highest possible effort, may be unstable
              """)
  
      #TODO: with modular flows does this go away?
      scparam(cfg, ['option','frontend'],
              sctype='str',
              scope='job',
              defvalue='verilog',
              shorthelp="Compilation frontend",
              switch="-frontend <frontend>",
              example=["cli: -frontend systemverilog",
                       "api: chip.set('option','frontend', 'systemverilog')"],
              schelp="""
              Specifies the frontend that flows should use for importing and
              processing source files. Default option is 'verilog', also supports
              'systemverilog' and 'chisel'. When using the Python API, this parameter
              must be configured before calling load_target().""")
  
      scparam(cfg, ['option','cfg'],
              sctype='[file]',
              scope='job',
              shorthelp="Configuration manifest",
              switch="-cfg <file>",
              example=["cli: -cfg mypdk.json",
                      "api: chip.set('option','cfg','mypdk.json')"],
              schelp="""
              List of filepaths to JSON formatted schema configuration
              manifests. The files are read in automatically when using the
              'sc' command line application. In Python programs, JSON manifests
              can be merged into the current working manifest using the
              read_manifest() method.""")
  
      key = 'default'
      scparam(cfg, ['option', 'env', key],
              sctype='str',
              scope='job',
              shorthelp="Environment variables",
              switch="-env 'key <str>",
              example=[
              "cli: -env 'PDK_HOME /disk/mypdk'",
              "api: chip.set('option','env', 'PDK_HOME', '/disk/mypdk')"],
              schelp="""
              Certain tools and reference flows require global environment
              variables to be set. These variables can be managed externally or
              specified through the env variable.""")
  
      scparam(cfg, ['option', 'scpath'],
              sctype='[dir]',
              scope='job',
              shorthelp="Search path",
              switch="-scpath <dir>",
              example=[
                  "cli: -scpath '/home/$USER/sclib'",
                  "api: chip.set('option', 'scpath','/home/$USER/sclib')"],
              schelp="""
              Specifies python modules paths for target import.""")
  
      scparam(cfg, ['option', 'loglevel'],
              sctype='str',
              scope='job',
              defvalue='INFO',
              shorthelp="Logging level",
              switch="-loglevel <str>",
              example=[
                  "cli: -loglevel INFO",
                  "api: chip.set('option', 'loglevel', 'INFO')"],
              schelp="""
              Provides explicit control over the level of debug logging printed.
              Valid entries include INFO, DEBUG, WARNING, ERROR.""")
  
      scparam(cfg, ['option', 'builddir'],
              sctype='dir',
              scope='job',
              defvalue='build',
              shorthelp="Build directory",
              switch="-builddir <dir>",
              example=[
                  "cli: -builddir ./build_the_future",
                  "api: chip.set('option', 'builddir','./build_the_future')"],
              schelp="""
              The default build directory is in the local './build' where SC was
              executed. The 'builddir' parameter can be used to set an alternate
              compilation directory path.""")
  
      scparam(cfg, ['option', 'jobname'],
              sctype='str',
              scope='job',
              defvalue='job0',
              shorthelp="Job name",
              switch="-jobname <str>",
              example=[
                  "cli: -jobname may1",
                  "api: chip.set('option','jobname','may1')"],
              schelp="""
              Jobname during invocation of run(). The jobname combined with a
              defined director structure (<dir>/<design>/<jobname>/<step>/<index>)
              enables multiple levels of transparent job, step, and index
              introspection.""")
  
      #TODO: remove?
      scparam(cfg, ['option','jobinput','default','default'],
              sctype='str',
              scope='job',
              shorthelp="Input job name",
              switch="-jobinput 'step index <str>'",
              example=[
                  "cli: -jobinput 'cts 0 job0'",
                  "api:  chip.set('option','jobinput','cts,'0','job0')"],
              schelp="""
              Specifies jobname inputs for the current run() on a per step
              and per index basis. During execution, the default behavior is to
              copy inputs from the current job.""")
  
      scparam(cfg, ['option', 'steplist'],
              sctype='[str]',
              scope='job',
              shorthelp="Compilation step list",
              switch="-steplist <step>",
              example=[
                  "cli: -steplist 'import'",
                  "api: chip.set('option','steplist','import')"],
              schelp="""
              List of steps to execute. The default is to execute all steps
              defined in the flow graph.""")
  
      scparam(cfg, ['option', 'skipstep'],
              sctype='[str]',
              scope='job',
              shorthelp="Skip step list",
              switch="-skipstep <str>",
              example=[
                  "cli: -skipstep lvs",
                  "api: chip.set('option','skipstep','lvs')"],
              schelp="""
              List of steps to skip during execution.The default is to
              execute all steps  defined in the flow graph.""")
  
      scparam(cfg, ['option', 'indexlist'],
              sctype='[str]',
              scope='job',
              shorthelp="Compilation index list",
              switch="-indexlist <index>",
              example=["cli: -indexlist 0",
                      "api: chip.set('option','indexlist','0')"],
              schelp="""
              List of indices to execute. The default is to execute all
              indices for each step of a run.""")
  
      scparam(cfg, ['option', 'bkpt'],
              sctype='[str]',
              scope='job',
              shorthelp="Breakpoint list",
              switch="-bkpt <str>",
              example=[
                  "cli: -bkpt place",
                  "api: chip.set('option,'bkpt','place')"],
              schelp="""
              List of step stop (break) points. If the step is a TCL
              based tool, then the breakpoints stops the flow inside the
              EDA tool. If the step is a command line tool, then the flow
              drops into a Python interpreter.""")
  
      scparam(cfg, ['option', 'msgevent'],
              sctype='[str]',
              scope='job',
              shorthelp="Message event trigger",
              switch="-msgevent <str>",
              example=["cli: -msgevent export",
                      "api: chip.set('option','msgevent','export')"],
              schelp="""
              A list of steps after which to notify a recipient. For
              example if values of syn, place, cts are entered separate
              messages would be sent after the completion of the syn,
              place, and cts steps.""")
  
      scparam(cfg, ['option', 'msgcontact'],
              sctype='[str]',
              scope='job',
              shorthelp="Message contact",
              switch="-msgcontact <str>",
              example=[
                  "cli: -msgcontact 'wile.e.coyote@acme.com'",
                  "api: chip.set('option','msgcontact','wiley@acme.com')"],
              schelp="""
              A list of phone numbers or email addresses to message
              on a event event within the msg_event param. Actual
              support for email and phone messages is platform
              dependent.""")
  
      filetype = 'default'
      scparam(cfg, ['option', 'showtool', filetype],
              sctype='str',
              scope='job',
              shorthelp="Select data display tool",
              switch="-showtool 'filetype <tool>'",
              example=["cli: -showtool 'gds klayout'",
                      "api: chip.set('option','showtool','gds','klayout')"],
              schelp="""
              Selects the tool to use by the show function for displaying
              the specified filetype.""")
  
      scparam(cfg, ['option', 'metricoff'],
              sctype='[str]',
              scope='job',
              shorthelp="Metric summary filter",
              switch="-metricoff '<str>'",
              example=[
                  "cli: -metricoff 'wirelength'",
                  "api: chip.set('option','metricoff','wirelength')"],
              schelp="""
              List of metrics to supress when printing out the run
              summary.""")
  
      # Booleans
      scparam(cfg, ['option', 'clean'],
              sctype='bool',
              scope='job',
              shorthelp="Clean up after run",
              switch="-clean <bool>",
              example=["cli: -clean",
                       "api: chip.set('option','clean',True)"],
              schelp="""
              Clean up all intermediate and non essential files at the end
              of a task, leaving the following:
  
              * log file
              * replay.sh
              * inputs/
              * outputs/
              * reports/
              * autogenerated manifests
              * any files generated by schema-specified regexes
              * files specified by :keypath:`tool, <tool>, keep`""")
  
      scparam(cfg, ['option', 'hash'],
              sctype='bool',
              scope='job',
              shorthelp="Enable file hashing",
              switch="-hash <bool>",
              example=["cli: -hash",
                       "api: chip.set('option','hash',True)"],
              schelp="""
              Enables hashing of all inputs and outputs during
              compilation. The hash values are stored in the hashvalue
              field of the individual parameters.""")
  
      scparam(cfg, ['option', 'nodisplay'],
              sctype='bool',
              scope='job',
              shorthelp="Headless execution",
              switch="-nodisplay <bool>",
              example=["cli: -nodisplay",
                       "api: chip.set('option','nodisplay',True)"],
              schelp="""
              The '-nodisplay' flag prevents SiliconCompiler from
              opening GUI windows such as the final metrics report.""")
  
      scparam(cfg, ['option', 'quiet'],
              sctype='bool',
              scope='job',
              shorthelp="Quiet execution",
              switch="-quiet <bool>",
              example=["cli: -quiet",
                      "api: chip.set('option','quiet',True)"],
              schelp="""
              The -quiet option forces all steps to print to a log file.
              This can be useful with Modern EDA tools which print
              significant content to the screen.""")
  
      scparam(cfg, ['option', 'jobincr'],
              sctype='bool',
              scope='job',
              shorthelp="Autoincrement jobname",
              switch="-jobincr <bool>",
              example=["cli: -jobincr",
                      "api: chip.set('option','jobincr',True)"],
              schelp="""
              Forces an auto-update of the jobname parameter if a directory
              matching the jobname is found in the build directory. If the
              jobname does not include a trailing digit, then a the number
              '1' is added to the jobname before updating the jobname
              parameter.""")
  
      scparam(cfg, ['option', 'novercheck'],
              sctype='bool',
              defvalue='false',
              scope='job',
              shorthelp="Disable version checking",
              switch="-novercheck <bool>",
              example=["cli: -novercheck",
                      "api: chip.set('option','novercheck',True)"],
              schelp="""
              Disables strict version checking on all invoked tools if True.
              The list of supported version numbers is defined in the
              'version' parameter in the 'eda' dictionary for each tool.""")
  
      scparam(cfg, ['option', 'relax'],
              sctype='bool',
              scope='job',
              shorthelp="Relax RTL linting",
              switch="-relax <bool>",
              example=["cli: -relax",
                      "api: chip.set('option','relax',True)"],
              schelp="""
              Specifies that tools should be lenient and suppress some
              warnings that may or may not indicate design issues.""")
  
      scparam(cfg, ['option', 'resume'],
              sctype='bool',
              scope='job',
              shorthelp="Resume build",
              switch="-resume <bool>",
              example=["cli: -resume",
                      "api: chip.set('option','resume',True)"],
              schelp="""
              If results exist for current job, then don't re-run any steps that
              had at least one index run successfully. Useful for debugging a
              flow that failed partway through.
              """)
  
      scparam(cfg, ['option', 'track'],
              sctype='bool',
              scope='job',
              shorthelp="Enable provenance tracking",
              switch="-track <bool>",
              example=["cli: -track",
                      "api: chip.set('option','track',True)"],
              schelp="""
              Turns on tracking of all 'record' parameters during each
              task. Tracking will result in potentially sensitive data
              being recorded in the manifest so only turn on this feature
              if you have control of the final manifest.""")
  
      scparam(cfg, ['option', 'trace'],
              sctype='bool',
              scope='job',
              shorthelp="Enable debug traces",
              switch="-trace <bool>",
              example=["cli: -trace",
                      "api: chip.set('option','trace',True)"],
              schelp="""
              Enables debug tracing during compilation and/or runtime.""")
  
      scparam(cfg, ['option', 'skipall'],
              sctype='bool',
              scope='job',
              shorthelp="Skip all tasks",
              switch="-skipall <bool>",
              example=["cli: -skipall",
                      "api: chip.set('option','skipall',True)"],
              schelp="""
              Skips the execution of all tools in run(), enabling a quick
              check of tool and setup without having to run through each
              step of a flow to completion.""")
  
      scparam(cfg, ['option', 'skipcheck'],
              sctype='bool',
              scope='job',
              shorthelp="Skip manifest check",
              switch="-skipcheck <bool>",
              example=["cli: -skipcheck",
                       "api: chip.set('option','skipcheck',True)"],
              schelp="""
              Bypasses the strict runtime manifest check. Can be used for
              accelerating initial bringup of tool/flow/pdk/libs targets.
              The flag should not be used for production compilation.""")
  
      scparam(cfg, ['option', 'copyall'],
              sctype='bool',
              scope='job',
              shorthelp="Copy all inputs to build directory",
              switch="-copyall <bool>",
              example=["cli: -copyall",
                      "api: chip.set('option','copyall',True)"],
              schelp="""
              Specifies that all used files should be copied into the
              build directory, overriding the per schema entry copy
              settings.""")
  
      scparam(cfg, ['option', 'show'],
              sctype='bool',
              scope='job',
              shorthelp="Show layout",
              switch="-show <bool>",
              example=["cli: -show",
                      "api: chip.set('option','show',True)"],
              schelp="""
              Specifies that the final hardware layout should be
              shown after the compilation has been completed. The
              final layout and tool used to display the layout is
              flow dependent.""")
  
      scparam(cfg, ['option', 'autoinstall'],
              sctype='bool',
              shorthelp=f"Option: auto install packages",
              switch=f"-autoinstall <bool>",
              example=[
                  f"cli: -autoinstall true'",
                  f"api: chip.set('option', 'autoinstall', True)"],
              schelp=f"""
              Enables automatic installation of missing dependencies from
              the registry.""")
  
      scparam(cfg, ['option', 'registry'],
              sctype='[dir]',
              shorthelp=f"Option: package registry",
              switch=f"-registry <dir>",
              example=[
                  f"cli: -registry '~/myregistry'",
                  f"api: chip.set('option','registry','~/myregistry')"],
              schelp=f"""
              List of Silicon Unified Packager (SUP) registry directories.
              Directories can be local file system folders or
              publicly available registries served up over http. The naming
              convention for registry packages is:
              <name>/<name>-<version>.json(.<gz>)?
              """)
  
      scparam(cfg,['option', 'entrypoint'],
              sctype='str',
              shorthelp="Program entry point",
              switch="-entrypoint <str>",
              example=["cli: -entrypoint top",
                      "api: chip.set('option', 'entrypoint', 'top')"],
              schelp="""Alternative entrypoint for compilation and
              simulation. The default entry point is 'design'.""")
  
      scparam(cfg,['option', 'idir'],
              sctype='[dir]',
              shorthelp="Design search paths",
              switch=['+incdir+<dir>', '-I <dir>'],
              example=[
                  "cli: +incdir+./mylib",
                  "api: chip.set('option','idir','./mylib')"],
              schelp="""
              Search paths to look for files included in the design using
              the ```include`` statement.""")
  
      scparam(cfg,['option', 'ydir'],
              sctype='[dir]',
              shorthelp="Design module search paths",
              switch='-y <dir>',
              example=[
                  "cli: -y './mylib'",
                  "api: chip.set('option','ydir','./mylib')"],
              schelp="""
              Search paths to look for verilog modules found in the the
              source list. The import engine will look for modules inside
              files with the specified +libext+ param suffix.""")
  
      scparam(cfg,['option', 'vlib'],
              sctype='[file]',
              shorthelp="Design libraries",
              switch='-v <file>',
              example=["cli: -v './mylib.v'",
                       "api: chip.set('option', 'vlib','./mylib.v')"],
              schelp="""
              List of library files to be read in. Modules found in the
              libraries are not interpreted as root modules.""")
  
      scparam(cfg,['option', 'define'],
              sctype='[str]',
              shorthelp="Design pre-processor symbol",
              switch="-D<str>",
              example=["cli: -DCFG_ASIC=1",
                       "api: chip.set('option','define','CFG_ASIC=1')"],
              schelp="""Symbol definition for source preprocessor.""")
  
      scparam(cfg,['option', 'libext'],
              sctype='[str]',
              shorthelp="Design file extensions",
              switch="+libext+<str>",
              example=[
                  "cli: +libext+sv",
                  "api: chip.set('option','libext','sv')"],
              schelp="""
              List of file extensions that should be used for finding modules.
              For example, if -y is specified as ./lib", and '.v' is specified as
              libext then the files ./lib/\\*.v ", will be searched for
              module matches.""")
  
      name = 'default'
      scparam(cfg,['option', 'param', name],
              sctype='str',
              shorthelp="Design parameter",
              switch="-param 'name <str>'",
              example=[
                  "cli: -param 'N 64'",
                  "api: chip.set('option','param','N','64')"],
              schelp="""
              Sets a top verilog level design module parameter. The value
              is limited to basic data literals. The parameter override is
              passed into tools such as Verilator and Yosys. The parameters
              support Verilog integer literals (64'h4, 2'b0, 4) and strings.
              Name of the top level module to compile.""")
  
  
      scparam(cfg,['option', 'cmdfile'],
              sctype='[file]',
              shorthelp="Design compilation command file",
              switch='-f <file>',
              example=["cli: -f design.f",
                       "api: chip.set('option', 'cmdfile','design.f')"],
              schelp="""
              Read the specified file, and act as if all text inside it was specified
              as command line parameters. Supported by most verilog simulators
              including Icarus and Verilator. The format of the file is not strongly
              standardized. Support for comments and environment variables within
              the file varies and depends on the tool used. SC simply passes on
              the filepath toe the tool executable.""")
  
      scparam(cfg,['option', 'flowcontinue'],
              sctype='bool',
              shorthelp="Continue flow on error",
              switch='-flowcontinue',
              example=["cli: -flowcontinue",
                       "api: chip.set('option', 'flowcontinue', True)"],
              schelp="""
              Continue executing flow after a tool logs errors. The default
              behavior is to quit executing the flow if a task ends and the errors
              metric is greater than 0. Note that the flow will always cease
              executing if the tool returns a nonzero status code. """)
  
      return cfg
  
  ############################################
  # Package information
  ############################################
  
  def schema_package(cfg):
  
      userid = 'default'
      module = 'default'
  
      scparam(cfg, ['package', 'depgraph', module],
              sctype='[(str,str)]',
              scope='global',
              shorthelp=f"Package dependency list",
              switch=f"-package_depgraph 'module <(str,str)>'",
              example=[
                  f"cli: -package_depgraph 'top (cpu,1.0.1)'",
                  f"api: chip.set('package','depgraph','top',('cpu','1.0.1'))"],
              schelp=f"""
              List of Silicon Unified Packager (SUP) dependencies
              used by the design specified on a per module basis a
              list of string tuples ('name','version').""")
  
      scparam(cfg,['package', 'name'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: name",
              switch=f"-package_name <str>",
              example=[
                  f"cli: -package_name yac",
                  f"api: chip.set('package','name','yac')"],
              schelp=f"""Package name.""")
  
      scparam(cfg,['package', 'version'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: version",
              switch=f"-package_version <str>",
              example=[
                  f"cli: -package_version 1.0",
                  f"api: chip.set('package','version','1.0')"],
              schelp=f"""Package version. Can be a branch, tag, commit hash,
              or a semver compatible version.""")
  
      scparam(cfg,['package', 'description'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: description",
              switch=f"-package_description <str>",
              example=[
                  f"cli: -package_description 'Yet another cpu'",
                  f"api: chip.set('package','description','Yet another cpu')"],
              schelp=f"""Package short one line description for package
              managers and summary reports.""")
  
      scparam(cfg,['package', 'keyword'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: keyword",
              switch=f"-package_keyword <str>",
              example=[
                  f"cli: -package_keyword cpu",
                  f"api: chip.set('package','keyword','cpu')"],
              schelp=f"""Package keyword(s) used to characterize package.""")
  
      scparam(cfg,['package', 'homepage'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: project homepage",
              switch=f"-package_homepage <str>",
              example=[
                  f"cli: -package_homepage index.html",
                  f"api: chip.set('package','homepage','index.html')"],
              schelp=f"""Package homepage.""")
  
      scparam(cfg,['package', 'doc', 'homepage'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: documentation homepage",
              switch=f"-package_doc_homepage <str>",
              example=[
                  f"cli: -package_doc_homepage index.html",
                  f"api: chip.set('package','doc', 'homepage','index.html')"],
              schelp=f"""
              Package documentation homepage. Filepath to design docs homepage.
              Complex designs can can include a long non standard list of
              documents dependent.  A single html entry point can be used to
              present an organized documentation dashboard to the designer.""")
  
      doctypes = ['datasheet',
                  'reference',
                  'userguide',
                  'quickstart',
                  'releasenotes',
                  'testplan',
                  'signoff',
                  'tutorial']
  
      for item in doctypes:
          scparam(cfg,['package', 'doc', item],
              sctype='[file]',
              scope='global',
              shorthelp=f"Package: {item} document",
              switch=f"-package_doc_{item} <str",
              example=[
                  f"cli: -package_doc_{item} {item}.pdf",
                  f"api: chip.set('package','doc',{item},'{item}.pdf')"],
              schelp=f""" Package list of {item} documents.""")
  
      scparam(cfg,['package', 'repo'],
              sctype='[str]',
              scope='global',
              shorthelp=f"Package: code repository",
              switch=f"-package_repo <str>",
              example=[
                  f"cli: -package_repo 'git@github.com:aolofsson/oh.git'",
                  f"api: chip.set('package','repo','git@github.com:aolofsson/oh.git')"],
              schelp=f"""Package IP address to source code repository.""")
  
      scparam(cfg,['package', 'dependency', module],
              sctype='[str]',
              scope='global',
              shorthelp=f"Package: version dependancies",
              switch=f"-package_dependency 'module <str>'",
              example=[
                  f"cli: -package_dependency 'hello 1.0'",
                  f"api: chip.set('package','dependency','hello','1.0')"],
              schelp=f"""Package dependencies specified as a key value pair.
              Versions shall follow the semver standard.""")
  
      scparam(cfg,['package', 'target'],
              sctype='[str]',
              scope='global',
              shorthelp=f"Package: qualified targets",
              switch=f"-package_target <str>",
              example=[
                  f"cli: -package_target 'asicflow_freepdk45'",
                  f"api: chip.set('package','target','asicflow_freepdk45')"],
              schelp=f"""Package list of qualified compilation targets.""")
  
      scparam(cfg,['package', 'license'],
              sctype='[str]',
              scope='global',
              shorthelp=f"Package: license identifiers",
              switch=f"-package_license <str>",
              example=[
                  f"cli: -package_license 'Apache-2.0'",
                  f"api: chip.set('package','license','Apache-2.0')"],
              schelp=f"""Package list of SPDX license identifiers.""")
  
      scparam(cfg,['package', 'licensefile'],
              sctype='[file]',
              scope='global',
              shorthelp=f"Package: license files",
              switch=f"-package_licensefile <file>",
              example=[
                  f"cli: -package_licensefile './LICENSE'",
                  f"api: chip.set('package','licensefile','./LICENSE')"],
              schelp=f"""Package list of license files for to be
              applied in cases when a SPDX identifier is not available.
              (eg. proprietary licenses).list of SPDX license identifiers.""")
  
      scparam(cfg,['package', 'location'],
              sctype='[str]',
              scope='global',
              shorthelp=f"Package: location",
              switch=f"-package_location <file>",
              example=[
                  f"cli: -package_location 'mars'",
                  f"api: chip.set('package','location','mars')"],
              schelp=f"""Package country of origin specified as standardized
              international country codes. The field can be left blank
              if the location is unknown or global.""")
  
      scparam(cfg,['package', 'organization'],
              sctype='[str]',
              scope='global',
              shorthelp=f"Package: sponsoring organization",
              switch=f"-package_organization <str>",
              example=[
                  f"cli: -package_organization 'humanity'",
                  f"api: chip.set('package','organization','humanity')"],
              schelp=f"""Package sponsoring organization. The field can be left
              blank if not applicable.""")
  
      scparam(cfg,['package', 'publickey'],
              sctype='str',
              scope='global',
              shorthelp=f"Package: public key",
              switch=f"-package_publickey <str>",
              example=[
                  f"cli: -package_publickey '6EB695706EB69570'",
                  f"api: chip.set('package','publickey','6EB695706EB69570')"],
              schelp=f"""Package public project key.""")
  
      record = ['name',
                'email',
                'username',
                'location',
                'organization',
                'publickey']
  
      for item in record:
          scparam(cfg,['package', 'author', userid, item],
                  sctype='str',
                  scope='global',
                  shorthelp=f"Package: author {item}",
                  switch=f"-package_author_{item} 'userid <str>'",
                  example=[
                      f"cli: -package_author_{item} 'wiley wiley@acme.com'",
                      f"api: chip.set('package','author','wiley','{item}','wiley@acme.com')"],
                  schelp=f"""Package author {item} provided with full name as key and
                  {item} as value.""")
  
      return cfg
  
  ############################################
  # Design Checklist
  ############################################
  
  def schema_checklist(cfg):
  
      item = 'default'
      standard = 'default'
      metric = 'default'
  
      scparam(cfg,['checklist', standard, item, 'description'],
              sctype='str',
              scope='global',
              shorthelp="Checklist: item description",
              switch="-checklist_description 'standard item <str>",
              example=[
                  "cli: -checklist_description 'ISO D000 A-DESCRIPTION'",
                  "api: chip.set('checklist','ISO','D000','description','A-DESCRIPTION')"],
              schelp="""
              A short one line description of the checklist item.""")
  
      scparam(cfg,['checklist', standard, item, 'requirement'],
              sctype='str',
              scope='global',
              shorthelp="Checklist: item requirement",
              switch="-checklist_requirement 'standard item <str>",
              example=[
                  "cli: -checklist_requirement 'ISO D000 DOCSTRING'",
                  "api: chip.set('checklist','ISO','D000','requirement','DOCSTRING')"],
              schelp="""
              A complete requirement description of the checklist item
              entered as a multi-line string.""")
  
      scparam(cfg,['checklist', standard, item, 'dataformat'],
              sctype='str',
              scope='global',
              shorthelp="Checklist: item data format",
              switch="-checklist_dataformat 'standard item <float>'",
              example=[
                  "cli: -checklist_dataformat 'ISO D000 dataformat README'",
                  "api: chip.set('checklist','ISO','D000','dataformat','README')"],
              schelp="""
              Free text description of the type of data files acceptable as
              checklist signoff validation.""")
  
      scparam(cfg,['checklist', standard, item, 'rationale'],
              sctype='[str]',
              scope='global',
              shorthelp="Checklist: item rational",
              switch="-checklist_rationale 'standard item <str>",
              example=[
                  "cli: -checklist_rational 'ISO D000 reliability'",
                  "api: chip.set('checklist','ISO','D000','rationale','reliability')"],
              schelp="""
              Rationale for the the checklist item. Rationale should be a
              unique alphanumeric code used by the standard or a short one line
              or single word description.""")
  
      scparam(cfg,['checklist', standard, item, 'criteria'],
              sctype='[str]',
              scope='global',
              shorthelp="Checklist: item criteria",
              switch="-checklist_criteria 'standard item <float>'",
              example=[
                  "cli: -checklist_criteria 'ISO D000 errors==0'",
                  "api: chip.set('checklist','ISO','D000','criteria','errors==0')"],
              schelp="""
              Simple list of signoff criteria for checklist item which
              must all be met for signoff. Each signoff criteria consists of
              a metric, a relational operator, and a value in the form.
              'metric op value'.""")
  
      scparam(cfg,['checklist', standard, item, 'task'],
              sctype='[(str,str,str)]',
              scope='global',
              shorthelp="Checklist: item task",
              switch="-checklist_task 'standard item <(str, str, str)>'",
              example=[
                  "cli: -checklist_task 'ISO D000 (job0,place,0)'",
                  "api: chip.set('checklist','ISO','D000','task',('job0','place','0'))"],
              schelp="""
              Flowgraph job and task used to verify the checklist item.
              The parameter should be left empty for manual and for tool
              flows that bypass the SC infrastructure.""")
  
      scparam(cfg,['checklist', standard, item, 'report'],
              sctype='[file]',
              scope='global',
              shorthelp="Checklist: item report",
              switch="-checklist_report 'standard item <file>'",
              example=[
                  "cli: -checklist_report 'ISO D000 my.rpt'",
                  "api: chip.set('checklist','ISO','D000','report','my.rpt')"],
              schelp="""
              Filepath to report(s) of specified type documenting the successful
              validation of the checklist item.""")
  
      scparam(cfg,['checklist', standard, item, 'waiver', metric],
              sctype='[file]',
              scope='global',
              shorthelp="Checklist: item metric waivers",
              switch="-checklist_waiver 'standard item metric <file>'",
              example=[
                  "cli: -checklist_waiver 'ISO D000 bold my.txt'",
                  "api: chip.set('checklist','ISO','D000','waiver','hold', 'my.txt')"],
              schelp="""
              Filepath to report(s) documenting waivers for the checklist
              item specified on a per metric basis.""")
  
      scparam(cfg,['checklist', standard, item, 'ok'],
              sctype='bool',
              scope='global',
              shorthelp="Checklist: item ok",
              switch="-checklist_ok 'standard item <str>'",
              example=[
                  "cli: -checklist_ok 'ISO D000 true'",
                  "api: chip.set('checklist','ISO','D000','ok', True)"],
              schelp="""
              Boolean check mark for the checklist item. A value of
              True indicates a human has inspected the all item dictionary
              parameters check out.""")
  
      return cfg
  
  ###########################
  # ASIC Setup
  ###########################
  
  def schema_asic(cfg):
      '''ASIC Automated Place and Route Parameters'''
  
      step = 'default'
      index = 'default'
  
      scparam(cfg, ['asic', 'stackup'],
              sctype='str',
              scope='job',
              shorthelp="ASIC Stackup target",
              switch="-asic_stackup <str>",
              example=["cli: -asic_stackup 2MA4MB2MC",
                       "api: chip.set('asic','stackup','2MA4MB2MC')"],
              schelp="""
              Target ASIC stackup to use in the design. The stackup is required
              parameter for PDKs with multiple metal stackups.""")
  
      scparam(cfg, ['asic', 'pdk'],
              sctype='str',
              scope='job',
              shorthelp="ASIC PDK target",
              switch="-asic_pdk <str>",
              example=["cli: -asic_pdk freepdk45",
                       "api: chip.set('asic','pdk','freepdk45')"],
              schelp="""
              Target ASIC PDK to use in the design.""")
  
      scparam(cfg, ['asic', 'logiclib'],
              sctype='[str]',
              scope='job',
              shorthelp="ASIC: logic libraries",
              switch="-asic_logiclib <str>",
              example=["cli: -asic_logiclib nangate45",
                       "api: chip.set('asic', 'logiclib','nangate45')"],
              schelp="""List of all selected logic libraries libraries
              to use for optimization for a given library architecture
              (9T, 11T, etc).""")
  
      scparam(cfg, ['asic', 'macrolib'],
              sctype='[str]',
              scope='job',
              shorthelp="ASIC: macro libraries",
              switch="-asic_macrolib <str>",
              example=["cli: -asic_macrolib sram64x1024",
                       "api: chip.set('asic', 'macrolib','sram64x1024')"],
              schelp="""
              List of macro libraries to be linked in during synthesis and place
              and route. Macro libraries are used for resolving instances but are
              not used as targets for logic synthesis.""")
  
      scparam(cfg, ['asic', 'delaymodel'],
              sctype='str',
              scope='job',
              shorthelp="ASIC: delay model",
              switch="-asic_delaymodel <str>",
              example= ["cli: -asic_delaymodel ccs",
                        "api: chip.set('asic', 'delaymodel', 'ccs')"],
              schelp="""
              Delay model to use for the target libs. Supported values
              are nldm and ccs.""")
  
      net = 'default'
      scparam(cfg, ['asic', 'ndr', net],
              sctype='(float,float)',
              scope='job',
              shorthelp="ASIC: non-default routing rule",
              switch="-asic_ndr 'netname <(float,float)>",
              example= ["cli: -asic_ndr 'clk (0.2,0.2)'",
                      "api: chip.set('asic','ndr','clk', (0.2,0.2))"],
              schelp="""
              Definitions of non-default routing rule specified on a per
              net basis. Constraints are entered as a (width,space) tuples
              specified in microns.""")
  
      scparam(cfg, ['asic', 'minlayer'],
              sctype='str',
              scope='job',
              shorthelp="ASIC: minimum routing layer",
              switch="-asic_minlayer <str>",
              example= ["cli: -asic_minlayer m2",
                      "api: chip.set('asic', 'minlayer', 'm2')"],
              schelp="""
              Minimum SC metal layer name to be used for automated place and route .
              Alternatively the layer can be a string that matches a layer hard coded
              in the pdk_aprtech file. Designers wishing to use the same setup across
              multiple process nodes should use the integer approach. For processes
              with ambiguous starting routing layers, exact strings should be used.
              """)
  
      scparam(cfg, ['asic', 'maxlayer'],
              sctype='str',
              scope='job',
              shorthelp="ASIC: maximum routing layer",
              switch="-asic_maxlayer <str>",
              example= ["cli: -asic_maxlayer m2",
                      "api: chip.set('asic', 'maxlayer', 'm2')"],
              schelp="""
              Maximum SC metal layer name to be used for automated place and route .
              Alternatively the layer can be a string that matches a layer hard coded
              in the pdk_aprtech file. Designers wishing to use the same setup across
              multiple process nodes should use the integer approach. For processes
              with ambiguous starting routing layers, exact strings should be used.
              """)
  
      scparam(cfg, ['asic', 'maxfanout'],
              sctype='int',
              scope='job',
              shorthelp="ASIC: maximum fanout",
              switch="-asic_maxfanout <int>",
              example= ["cli: -asic_maxfanout 64",
                      "api: chip.set('asic', 'maxfanout', '64')"],
              schelp="""
               Maximum driver fanout allowed during automated place and route.
              The parameter directs the APR tool to break up any net with fanout
              larger than maxfanout into sub nets and buffer.""")
  
      scparam(cfg, ['asic', 'maxlength'],
              sctype='float',
              unit='um',
              scope='job',
              shorthelp="ASIC: maximum wire length",
              switch="-asic_maxlength <float>",
              example= ["cli: -asic_maxlength 1000",
                      "api: chip.set('asic', 'maxlength', '1000')"],
              schelp="""
              Maximum total wire length allowed in design during APR. Any
              net that is longer than maxlength is broken up into segments by
              the tool.""")
  
      scparam(cfg, ['asic', 'maxcap'],
              sctype='float',
              unit='F',
              scope='job',
              shorthelp="ASIC: maximum net capacitance",
              switch="-asic_maxcap <float>",
              example= ["cli: -asic_maxcap '0.25e-12'",
                        "api: chip.set('asic', 'maxcap', '0.25e-12')"],
              schelp="""Maximum allowed capacitance per net.""")
  
      scparam(cfg, ['asic', 'maxslew'],
              sctype='float',
              unit='s',
              scope='job',
              shorthelp="ASIC: maximum slew",
              switch="-asic_maxslew <float>",
              example= ["cli: -asic_maxslew '0.25e-9'",
                      "api: chip.set('asic', 'maxslew', '0.25e-9')"],
              schelp="""Maximum allowed transition time per net.""")
  
      sigtype='default'
      scparam(cfg, ['asic', 'rclayer', sigtype],
              sctype='str',
              scope='job',
              shorthelp="ASIC: parasitics layer",
              switch="-asic_rclayer 'sigtype <str>'",
              example= ["cli: -asic_rclayer 'clk m3'",
                      "api: chip.set('asic', 'rclayer', 'clk', 'm3')"],
              schelp="""
              Technology agnostic metal layer to be used for parasitic
              extraction estimation during APR for the wire type specified
              Current the supported wire types are: clk, data. The metal
              layers can be specified as technology agnostic SC layers
              starting with m1 or as hard PDK specific layer names.""")
  
      scparam(cfg, ['asic', 'vpinlayer'],
              sctype='str',
              scope='job',
              shorthelp="ASIC: vertical pin layer",
              switch="-asic_vpinlayer <str>",
              example= ["cli: -asic_vpinlayer m3",
                      "api: chip.set('asic', 'vpinlayer', 'm3')"],
              schelp="""
              Metal layer to use for automated vertical pin placement
              during APR.  The metal layers can be specified as technology
              agnostic SC layers starting with m1 or as hard PDK specific
              layer names.""")
  
      scparam(cfg, ['asic', 'hpinlayer'],
              sctype='str',
              scope='job',
              shorthelp="ASIC: vertical pin layer",
              switch="-asic_hpinlayer <str>",
              example= ["cli: -asic_hpinlayer m4",
                      "api: chip.set('asic', 'hpinlayer', 'm4')"],
              schelp="""
              Metal layer to use for automated horizontal pin placement
              during APR.  The metal layers can be specified as technology
              agnostic SC layers starting with m1 or as hard PDK specific
              layer names.""")
  
      scparam(cfg, ['asic', 'density'],
              sctype='float',
              scope='job',
              shorthelp="ASIC: target core density",
              switch="-asic_density <float>",
              example= ["cli: -asic_density 30",
                        "api: chip.set('asic', 'density', '30')"],
              schelp="""
              Target density based on the total design cell area reported
              after synthesis. This number is used when no diearea or floorplan is
              supplied. Any number between 1 and 100 is legal, but values above 50
              may fail due to area/congestion issues during apr.""")
  
      scparam(cfg, ['asic', 'coremargin'],
              sctype='float',
              unit='um',
              scope='job',
              shorthelp="ASIC: block core margin",
              switch="-asic_coremargin <float>",
              example= ["cli: -asic_coremargin 1",
                        "api: chip.set('asic', 'coremargin', '1')"],
              schelp="""
              Halo/margin between the die boundary and core placement for
              automated floorplanning when no diearea or floorplan is
              supplied.""")
  
      scparam(cfg, ['asic', 'aspectratio'],
              sctype='float',
              scope='job',
              shorthelp="ASIC: block aspect ratio",
              switch="-asic_aspectratio <float>",
              example= ["cli: -asic_aspectratio 2.0",
                      "api: chip.set('asic', 'aspectratio', '2.0')"],
              schelp="""
              Height to width ratio of the block for automated floor-planning.
              Values below 0.1 and above 10 should be avoided as they will likely fail
              to converge during placement and routing. The ideal aspect ratio for
              most designs is 1. This value is only used when no diearea or floorplan
              is supplied.""")
  
      scparam(cfg, ['asic', 'diearea'],
              sctype='[(float,float)]',
              unit='um',
              scope='job',
              shorthelp="ASIC: die area outline",
              switch="-asic_diearea <[(float,float)]>",
              example= ["cli: -asic_diearea '(0,0)'",
                      "api: chip.set('asic', 'diearea', (0,0))"],
              schelp="""
              List of (x,y) points that define the outline of the die area for the
              physical design. Simple rectangle areas can be defined with two points,
              one for the lower left corner and one for the upper right corner. All
              values are specified in microns.""")
  
      scparam(cfg, ['asic', 'corearea'],
              sctype='[(float,float)]',
              unit='um',
              scope='job',
              shorthelp="ASIC: core area outline",
              switch="-asic_corearea <[(float,float)]>",
              example= ["cli: -asic_corearea '(0,0)'",
                      "api: chip.set('asic', 'corearea', (0,0))"],
              schelp="""
              List of (x,y) points that define the outline of the core area for the
              physical design. Simple rectangle areas can be defined with two points,
              one for the lower left corner and one for the upper right corner. All
              values are specified in microns.""")
  
      tool = 'default'
      key = 'default'
      scparam(cfg, ['asic', 'file', tool, key],
              sctype='[file]',
              shorthelp="ASIC: special file",
              switch="-asic_file 'tool key<file>'",
              example=[
                  "cli: -asic_file 'yosys presyn ~/presyn.tcl'",
                  "api: chip.set('asic','file','yosys','presyn','~/presyn.tcl')"],
              schelp="""
              List of named files specified on a per tool basis.
              The parameter should only be used for specifying files that are
              not directly supported by the ASIC schema.""")
  
      scparam(cfg, ['asic', 'dir', tool, key],
              sctype='[dir]',
              shorthelp="ASIC: special directory",
              switch="-asic_dir 'tool key <dir>'",
              example=[
                  "cli: -asic_dir 'lib atool db ~/libdb'",
                  "api: chip.set('asic','dir','atool','db','~/libdb')"],
              schelp="""
              List of named dirs specified on a per tool basis. The parameter
              should only be used for specifying dirs that are not directly
              supported by the ASIC schema.""")
  
      scparam(cfg, ['asic', 'var', tool, key],
              sctype='[str]',
              shorthelp="ASIC: special variable",
              switch="-asic_variable 'tool key <str>'",
              example=[
                  "cli: -asic_variable 'xyce modeltype bsim4'""",
                  "api: chip.set('asic','var','xyce','modeltype','bsim4')"],
              schelp="""
              List of key/value strings specified on a per basis. The parameter
              should only be used for specifying variables that are
              not directly supported by the SiliconCompiler PDK schema.""")
  
  
      # TODO: Expand on the exact definitions of these types of cells.
      # minimize typing
      names = ['driver',
               'load',
               'buf',
               'decap',
               'delay',
               'tie',
               'hold',
               'clkbuf',
               'clkdelay',
               'clkinv',
               'clkgate',
               'clkicg',
               'clklogic',
               'ignore',
               'filler',
               'tap',
               'endcap',
               'antenna']
  
      for item in names:
          scparam(cfg, ['asic', 'cells', item],
                  sctype='[str]',
                  shorthelp=f"ASIC: {item} cell list",
                  switch=f"-asic_cells_{item} '<str>'",
                  example=[
                      f"cli: -asic_cells_{item} '*eco*'",
                      f"api: chip.set('asic','cells',{item},'*eco*')"],
                  schelp="""
                  List of cells grouped by a property that can be accessed
                  directly by the designer and tools. The example below shows how
                  all cells containing the string 'eco' could be marked as dont use
                  for the tool.""")
  
      # Place and route parameters (optional)
      scparam(cfg, ['asic', 'pgmetal'],
              sctype='str',
              shorthelp="ASIC: powergrid layer",
              switch="-asic_pgmetal '<str>'",
              example=["cli: -asic_pgmetal m1",
                      "api: chip.set('asic','pgmetal','m1')"],
              schelp="""
              Top metal layer used for power and ground routing within the
              library. The parameter can be used to guide cell power grid
              hookup by APR tools.""")
  
      scparam(cfg,['asic', 'libarch'],
              sctype='str',
              shorthelp="ASIC: library architecture",
              switch="-asic_libarch '<str>'",
              example=[
                  "cli: -asic_libarch '12track'",
                  "api: chip.set('asic','libarch','12track')"],
              schelp="""
              The library architecture (e.g. library height) used to build the
              design. For example a PDK with support for 9 and 12 track libraries
              might have 'libarchs' called 9t and 12t.""")
  
      # footprint
      key = 'default'
      scparam(cfg,['asic', 'footprint', key, 'alias'],
              sctype='[str]',
              shorthelp="ASIC: Footprint name aliases",
              switch="-asic_footprint_alias 'key <str>'",
              example=[
                  "cli: -asic_footprint_alias '12track FreeCell'",
                  "api: chip.set('asic','footprint','12track','alias','FreeCell')"],
              schelp="""
              Alias for the footprint key that is sometimes needed when the footprint can
              be referenced by multiple names. The key is the 'official' footprint.""")
  
      scparam(cfg, ['asic', 'footprint', key, 'symmetry'],
              sctype='str',
              shorthelp="ASIC: Footprint symmetry",
              switch="-asic_footprint_symmetry 'key <str>'",
              example=[
                  "cli: -asic_footprint_symmetry 'core X Y'",
                  "api: chip.set('asic','footprint','core','symmetry','X Y')"],
              schelp="""
              Footprint symmetry based on LEF standard definition. 'X' implies
              symmetric about the x axis, 'Y' implies symmetry about the y axis, and
              'X Y' implies symmetry about the x and y axis.""")
  
  
      scparam(cfg, ['asic', 'footprint', key, 'size'],
              sctype='(float,float)',
              shorthelp="ASIC: Footprint size",
              switch="-asic_footprint_size 'key <str>'",
              example=[
                  "cli: -asic_footprint_size 'core (1.0,1.0)'",
                  "api: chip.set('asic','footprint','core','size',(1.0,1.0))"],
              schelp="""
              Size of the footprint described as a (width, height) tuple in
              microns.""")
  
  
      return cfg
  
  ############################################
  # Constraints
  ############################################
  
  def schema_constraint(cfg, scenario='default'):
  
      scparam(cfg,['constraint', scenario, 'voltage'],
              sctype='float',
              unit='V',
              scope='job',
              shorthelp="Constraint voltage level",
              switch="-constraint_voltage 'scenario <float>'",
              example=["cli: -constraint_voltage 'worst 0.9'",
                       "api: chip.set('constraint', 'worst','voltage', '0.9')"],
              schelp="""Operating voltage applied to the scenario.""")
  
      scparam(cfg,['constraint', scenario, 'temperature'],
              sctype='float',
              scope='job',
              shorthelp="Constraint temperature",
              switch="-constraint_temperature 'scenario <float>'",
              example=["cli: -constraint_temperature 'worst 125'",
                       "api: chip.set('constraint', 'worst', 'temperature','125')"],
              schelp="""Chip temperature applied to the scenario specified in degrees C.""")
  
      scparam(cfg,['constraint', scenario, 'libcorner'],
              sctype='str',
              scope='job',
              shorthelp="Constraint library corner",
              switch="-constraint_libcorner 'scenario <str>'",
              example=["cli: -constraint_libcorner 'worst ttt'",
                      "api: chip.set('constraint', 'worst', 'libcorner', 'ttt')"],
              schelp="""Library corner applied to the scenario to scale
              library timing models based on the libcorner value for models
              that support it. The parameter is ignored for libraries that
              have one hard coded model per libcorner.""")
  
      scparam(cfg,['constraint', scenario, 'pexcorner'],
              sctype='str',
              scope='job',
              shorthelp="Constraint pex corner",
              switch="-constraint_pexcorner 'scenario <str>'",
              example=["cli: -constraint_pexcorner 'worst max'",
                      "api: chip.set('constraint', 'worst', 'pexcorner', 'max')"],
              schelp="""Parasitic corner applied to the scenario. The
              'pexcorner' string must match a corner found in the pdk
              pexmodel setup.""")
  
      scparam(cfg,['constraint', scenario, 'opcond'],
              sctype='str',
              scope='job',
              shorthelp="Constraint operating condition",
              switch="-constraint_opcond 'scenario <str>'",
              example=["cli: -constraint_opcond 'worst typical_1.0'",
                       "api: chip.set('constraint', 'worst', 'opcond',  'typical_1.0')"],
              schelp="""Operating condition applied to the scenario. The value
              can be used to access specific conditions within the library
              timing models from the 'logiclib' timing models.""")
  
      scparam(cfg,['constraint', scenario, 'mode'],
              sctype='str',
              scope='job',
              shorthelp="Constraint operating mode",
              switch="-constraint_mode 'scenario <str>'",
              example=["cli: -constraint_mode 'worst test'",
                       "api: chip.set('constraint',  'worst','mode', 'test')"],
              schelp="""Operating mode for the scenario. Operating mode strings
              can be values such as test, functional, standby.""")
  
      scparam(cfg,['constraint', scenario, 'file'],
              sctype='[file]',
              scope='job',
              copy='true',
              shorthelp="Constraint files",
              switch="-constraint_file 'scenario <file>'",
              example=["cli: -constraint_file 'worst hello.sdc'",
                       "api: chip.set('constraint','worst','file', 'hello.sdc')"],
              schelp="""List of timing constraint files to use for the scenario. The
              values are combined with any constraints specified by the design
              'constraint' parameter. If no constraints are found, a default
              constraint file is used based on the clock definitions.""")
  
      scparam(cfg,['constraint', scenario, 'check'],
              sctype='[str]',
              scope='job',
              shorthelp="Constraint checks to perform",
              switch="-constraint_check 'scenario <str>'",
              example=["cli: -constraint_check 'worst check setup'",
                      "api: chip.add('constraint','worst','check','setup')"],
              schelp="""
              List of checks for to perform for the scenario. The checks must
              align with the capabilities of the EDA tools and flow being used.
              Checks generally include objectives like meeting setup and hold goals
              and minimize power. Standard check names include setup, hold, power,
              noise, reliability.""")
  
      return cfg
  
  ##############################################################################
  # Main routine
  if __name__ == "__main__":
      cfg = schema_cfg()
      print(json.dumps(cfg, indent=4, sort_keys=True))
  
files: siliconcompiler/schema.py
files: {
      "arg": {
          "flow": {
              "default": {
                  "defvalue": [],
                  "example": [
                      "cli: -arg_flow 'n 100'",
                      "api: chip.set('arg','flow','n', 100)"
                  ],
                  "help": "Parameter passed in as key/value pair to the flow target\nreferenced in the load_flow() API call. See the target flow\nfor specific guidelines regarding configuration parameters.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "scratch",
                  "shorthelp": "ARG: Flow argument",
                  "signature": [],
                  "switch": "-arg_flow 'key <str>'",
                  "type": "[str]",
                  "value": []
              }
          },
          "index": {
              "defvalue": null,
              "example": [
                  "cli: -arg_index 0",
                  "api: chip.set('arg','index','0')"
              ],
              "help": "Dynamic parameter passed in by the sc runtime as an argument to\na runtime task. The parameter enables configuration code\n(usually TCL) to use control flow that depend on the current\n'index'. The parameter is used the run() function and\nis not intended for external use.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "scratch",
              "shorthelp": "ARG: Index argument",
              "signature": null,
              "switch": "-arg_index <str>",
              "type": "str",
              "value": null
          },
          "pdk": {
              "default": {
                  "defvalue": [],
                  "example": [
                      "cli: -arg_pdk 'mimcap true'",
                      "api: chip.set('arg','pdk','mimcap','true')"
                  ],
                  "help": "Parameter passed in as key/value pair to the technology target\nreferenced in the load_pdk() API call. See the target technology\nfor specific guidelines regarding configuration parameters.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "scratch",
                  "shorthelp": "ARG: PDK argument",
                  "signature": [],
                  "switch": "-arg_pdk 'key <str>",
                  "type": "[str]",
                  "value": []
              }
          },
          "step": {
              "defvalue": null,
              "example": [
                  "cli: -arg_step 'route'",
                  "api: chip.set('arg', 'step', 'route')"
              ],
              "help": "Dynamic parameter passed in by the sc runtime as an argument to\na runtime task. The parameter enables configuration code\n(usually TCL) to use control flow that depend on the current\n'step'. The parameter is used the run() function and\nis not intended for external use.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "scratch",
              "shorthelp": "ARG: Step argument",
              "signature": null,
              "switch": "-arg_step <str>",
              "type": "str",
              "value": null
          }
      },
      "asic": {
          "aspectratio": {
              "defvalue": null,
              "example": [
                  "cli: -asic_aspectratio 2.0",
                  "api: chip.set('asic', 'aspectratio', '2.0')"
              ],
              "help": "Height to width ratio of the block for automated floor-planning.\nValues below 0.1 and above 10 should be avoided as they will likely fail\nto converge during placement and routing. The ideal aspect ratio for\nmost designs is 1. This value is only used when no diearea or floorplan\nis supplied.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: block aspect ratio",
              "signature": null,
              "switch": "-asic_aspectratio <float>",
              "type": "float",
              "value": null
          },
          "cells": {
              "antenna": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_antenna '*eco*'",
                      "api: chip.set('asic','cells',antenna,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: antenna cell list",
                  "signature": [],
                  "switch": "-asic_cells_antenna '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "buf": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_buf '*eco*'",
                      "api: chip.set('asic','cells',buf,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: buf cell list",
                  "signature": [],
                  "switch": "-asic_cells_buf '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "clkbuf": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_clkbuf '*eco*'",
                      "api: chip.set('asic','cells',clkbuf,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: clkbuf cell list",
                  "signature": [],
                  "switch": "-asic_cells_clkbuf '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "clkdelay": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_clkdelay '*eco*'",
                      "api: chip.set('asic','cells',clkdelay,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: clkdelay cell list",
                  "signature": [],
                  "switch": "-asic_cells_clkdelay '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "clkgate": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_clkgate '*eco*'",
                      "api: chip.set('asic','cells',clkgate,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: clkgate cell list",
                  "signature": [],
                  "switch": "-asic_cells_clkgate '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "clkicg": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_clkicg '*eco*'",
                      "api: chip.set('asic','cells',clkicg,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: clkicg cell list",
                  "signature": [],
                  "switch": "-asic_cells_clkicg '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "clkinv": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_clkinv '*eco*'",
                      "api: chip.set('asic','cells',clkinv,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: clkinv cell list",
                  "signature": [],
                  "switch": "-asic_cells_clkinv '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "clklogic": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_clklogic '*eco*'",
                      "api: chip.set('asic','cells',clklogic,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: clklogic cell list",
                  "signature": [],
                  "switch": "-asic_cells_clklogic '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "decap": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_decap '*eco*'",
                      "api: chip.set('asic','cells',decap,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: decap cell list",
                  "signature": [],
                  "switch": "-asic_cells_decap '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "delay": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_delay '*eco*'",
                      "api: chip.set('asic','cells',delay,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: delay cell list",
                  "signature": [],
                  "switch": "-asic_cells_delay '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "driver": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_driver '*eco*'",
                      "api: chip.set('asic','cells',driver,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: driver cell list",
                  "signature": [],
                  "switch": "-asic_cells_driver '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "endcap": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_endcap '*eco*'",
                      "api: chip.set('asic','cells',endcap,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: endcap cell list",
                  "signature": [],
                  "switch": "-asic_cells_endcap '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "filler": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_filler '*eco*'",
                      "api: chip.set('asic','cells',filler,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: filler cell list",
                  "signature": [],
                  "switch": "-asic_cells_filler '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "hold": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_hold '*eco*'",
                      "api: chip.set('asic','cells',hold,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: hold cell list",
                  "signature": [],
                  "switch": "-asic_cells_hold '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "ignore": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_ignore '*eco*'",
                      "api: chip.set('asic','cells',ignore,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: ignore cell list",
                  "signature": [],
                  "switch": "-asic_cells_ignore '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "load": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_load '*eco*'",
                      "api: chip.set('asic','cells',load,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: load cell list",
                  "signature": [],
                  "switch": "-asic_cells_load '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "tap": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_tap '*eco*'",
                      "api: chip.set('asic','cells',tap,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: tap cell list",
                  "signature": [],
                  "switch": "-asic_cells_tap '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "tie": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_tie '*eco*'",
                      "api: chip.set('asic','cells',tie,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: tie cell list",
                  "signature": [],
                  "switch": "-asic_cells_tie '<str>'",
                  "type": "[str]",
                  "value": []
              }
          },
          "corearea": {
              "defvalue": [],
              "example": [
                  "cli: -asic_corearea '(0,0)'",
                  "api: chip.set('asic', 'corearea', (0,0))"
              ],
              "help": "List of (x,y) points that define the outline of the core area for the\nphysical design. Simple rectangle areas can be defined with two points,\none for the lower left corner and one for the upper right corner. All\nvalues are specified in microns.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: core area outline",
              "signature": [],
              "switch": "-asic_corearea <[(float,float)]>",
              "type": "[(float,float)]",
              "unit": "um",
              "value": []
          },
          "coremargin": {
              "defvalue": null,
              "example": [
                  "cli: -asic_coremargin 1",
                  "api: chip.set('asic', 'coremargin', '1')"
              ],
              "help": "Halo/margin between the die boundary and core placement for\nautomated floorplanning when no diearea or floorplan is\nsupplied.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: block core margin",
              "signature": null,
              "switch": "-asic_coremargin <float>",
              "type": "float",
              "unit": "um",
              "value": null
          },
          "delaymodel": {
              "defvalue": null,
              "example": [
                  "cli: -asic_delaymodel ccs",
                  "api: chip.set('asic', 'delaymodel', 'ccs')"
              ],
              "help": "Delay model to use for the target libs. Supported values\nare nldm and ccs.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: delay model",
              "signature": null,
              "switch": "-asic_delaymodel <str>",
              "type": "str",
              "value": null
          },
          "density": {
              "defvalue": null,
              "example": [
                  "cli: -asic_density 30",
                  "api: chip.set('asic', 'density', '30')"
              ],
              "help": "Target density based on the total design cell area reported\nafter synthesis. This number is used when no diearea or floorplan is\nsupplied. Any number between 1 and 100 is legal, but values above 50\nmay fail due to area/congestion issues during apr.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: target core density",
              "signature": null,
              "switch": "-asic_density <float>",
              "type": "float",
              "value": null
          },
          "diearea": {
              "defvalue": [],
              "example": [
                  "cli: -asic_diearea '(0,0)'",
                  "api: chip.set('asic', 'diearea', (0,0))"
              ],
              "help": "List of (x,y) points that define the outline of the die area for the\nphysical design. Simple rectangle areas can be defined with two points,\none for the lower left corner and one for the upper right corner. All\nvalues are specified in microns.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: die area outline",
              "signature": [],
              "switch": "-asic_diearea <[(float,float)]>",
              "type": "[(float,float)]",
              "unit": "um",
              "value": []
          },
          "dir": {
              "default": {
                  "default": {
                      "defvalue": [],
                      "example": [
                          "cli: -asic_dir 'lib atool db ~/libdb'",
                          "api: chip.set('asic','dir','atool','db','~/libdb')"
                      ],
                      "help": "List of named dirs specified on a per tool basis. The parameter\nshould only be used for specifying dirs that are not directly\nsupported by the ASIC schema.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "ASIC: special directory",
                      "signature": [],
                      "switch": "-asic_dir 'tool key <dir>'",
                      "type": "[dir]",
                      "value": []
                  }
              }
          },
          "file": {
              "default": {
                  "default": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -asic_file 'yosys presyn ~/presyn.tcl'",
                          "api: chip.set('asic','file','yosys','presyn','~/presyn.tcl')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "List of named files specified on a per tool basis.\nThe parameter should only be used for specifying files that are\nnot directly supported by the ASIC schema.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "ASIC: special file",
                      "signature": [],
                      "switch": "-asic_file 'tool key<file>'",
                      "type": "[file]",
                      "value": []
                  }
              }
          },
          "footprint": {
              "default": {
                  "alias": {
                      "defvalue": [],
                      "example": [
                          "cli: -asic_footprint_alias '12track FreeCell'",
                          "api: chip.set('asic','footprint','12track','alias','FreeCell')"
                      ],
                      "help": "Alias for the footprint key that is sometimes needed when the footprint can\nbe referenced by multiple names. The key is the 'official' footprint.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "ASIC: Footprint name aliases",
                      "signature": [],
                      "switch": "-asic_footprint_alias 'key <str>'",
                      "type": "[str]",
                      "value": []
                  },
                  "size": {
                      "defvalue": null,
                      "example": [
                          "cli: -asic_footprint_size 'core (1.0,1.0)'",
                          "api: chip.set('asic','footprint','core','size',(1.0,1.0))"
                      ],
                      "help": "Size of the footprint described as a (width, height) tuple in\nmicrons.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "ASIC: Footprint size",
                      "signature": null,
                      "switch": "-asic_footprint_size 'key <str>'",
                      "type": "(float,float)",
                      "value": null
                  },
                  "symmetry": {
                      "defvalue": null,
                      "example": [
                          "cli: -asic_footprint_symmetry 'core X Y'",
                          "api: chip.set('asic','footprint','core','symmetry','X Y')"
                      ],
                      "help": "Footprint symmetry based on LEF standard definition. 'X' implies\nsymmetric about the x axis, 'Y' implies symmetry about the y axis, and\n'X Y' implies symmetry about the x and y axis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "ASIC: Footprint symmetry",
                      "signature": null,
                      "switch": "-asic_footprint_symmetry 'key <str>'",
                      "type": "str",
                      "value": null
                  }
              }
          },
          "hpinlayer": {
              "defvalue": null,
              "example": [
                  "cli: -asic_hpinlayer m4",
                  "api: chip.set('asic', 'hpinlayer', 'm4')"
              ],
              "help": "Metal layer to use for automated horizontal pin placement\nduring APR.  The metal layers can be specified as technology\nagnostic SC layers starting with m1 or as hard PDK specific\nlayer names.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: vertical pin layer",
              "signature": null,
              "switch": "-asic_hpinlayer <str>",
              "type": "str",
              "value": null
          },
          "libarch": {
              "defvalue": null,
              "example": [
                  "cli: -asic_libarch '12track'",
                  "api: chip.set('asic','libarch','12track')"
              ],
              "help": "The library architecture (e.g. library height) used to build the\ndesign. For example a PDK with support for 9 and 12 track libraries\nmight have 'libarchs' called 9t and 12t.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: library architecture",
              "signature": null,
              "switch": "-asic_libarch '<str>'",
              "type": "str",
              "value": null
          },
          "logiclib": {
              "defvalue": [],
              "example": [
                  "cli: -asic_logiclib nangate45",
                  "api: chip.set('asic', 'logiclib','nangate45')"
              ],
              "help": "List of all selected logic libraries libraries\nto use for optimization for a given library architecture\n(9T, 11T, etc).",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: logic libraries",
              "signature": [],
              "switch": "-asic_logiclib <str>",
              "type": "[str]",
              "value": []
          },
          "macrolib": {
              "defvalue": [],
              "example": [
                  "cli: -asic_macrolib sram64x1024",
                  "api: chip.set('asic', 'macrolib','sram64x1024')"
              ],
              "help": "List of macro libraries to be linked in during synthesis and place\nand route. Macro libraries are used for resolving instances but are\nnot used as targets for logic synthesis.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: macro libraries",
              "signature": [],
              "switch": "-asic_macrolib <str>",
              "type": "[str]",
              "value": []
          },
          "maxcap": {
              "defvalue": null,
              "example": [
                  "cli: -asic_maxcap '0.25e-12'",
                  "api: chip.set('asic', 'maxcap', '0.25e-12')"
              ],
              "help": "Maximum allowed capacitance per net.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: maximum net capacitance",
              "signature": null,
              "switch": "-asic_maxcap <float>",
              "type": "float",
              "unit": "F",
              "value": null
          },
          "maxfanout": {
              "defvalue": null,
              "example": [
                  "cli: -asic_maxfanout 64",
                  "api: chip.set('asic', 'maxfanout', '64')"
              ],
              "help": " Maximum driver fanout allowed during automated place and route.\nThe parameter directs the APR tool to break up any net with fanout\nlarger than maxfanout into sub nets and buffer.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: maximum fanout",
              "signature": null,
              "switch": "-asic_maxfanout <int>",
              "type": "int",
              "value": null
          },
          "maxlayer": {
              "defvalue": null,
              "example": [
                  "cli: -asic_maxlayer m2",
                  "api: chip.set('asic', 'maxlayer', 'm2')"
              ],
              "help": "Maximum SC metal layer name to be used for automated place and route .\nAlternatively the layer can be a string that matches a layer hard coded\nin the pdk_aprtech file. Designers wishing to use the same setup across\nmultiple process nodes should use the integer approach. For processes\nwith ambiguous starting routing layers, exact strings should be used.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: maximum routing layer",
              "signature": null,
              "switch": "-asic_maxlayer <str>",
              "type": "str",
              "value": null
          },
          "maxlength": {
              "defvalue": null,
              "example": [
                  "cli: -asic_maxlength 1000",
                  "api: chip.set('asic', 'maxlength', '1000')"
              ],
              "help": "Maximum total wire length allowed in design during APR. Any\nnet that is longer than maxlength is broken up into segments by\nthe tool.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: maximum wire length",
              "signature": null,
              "switch": "-asic_maxlength <float>",
              "type": "float",
              "unit": "um",
              "value": null
          },
          "maxslew": {
              "defvalue": null,
              "example": [
                  "cli: -asic_maxslew '0.25e-9'",
                  "api: chip.set('asic', 'maxslew', '0.25e-9')"
              ],
              "help": "Maximum allowed transition time per net.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: maximum slew",
              "signature": null,
              "switch": "-asic_maxslew <float>",
              "type": "float",
              "unit": "s",
              "value": null
          },
          "minlayer": {
              "defvalue": null,
              "example": [
                  "cli: -asic_minlayer m2",
                  "api: chip.set('asic', 'minlayer', 'm2')"
              ],
              "help": "Minimum SC metal layer name to be used for automated place and route .\nAlternatively the layer can be a string that matches a layer hard coded\nin the pdk_aprtech file. Designers wishing to use the same setup across\nmultiple process nodes should use the integer approach. For processes\nwith ambiguous starting routing layers, exact strings should be used.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: minimum routing layer",
              "signature": null,
              "switch": "-asic_minlayer <str>",
              "type": "str",
              "value": null
          },
          "ndr": {
              "default": {
                  "defvalue": null,
                  "example": [
                      "cli: -asic_ndr 'clk (0.2,0.2)'",
                      "api: chip.set('asic','ndr','clk', (0.2,0.2))"
                  ],
                  "help": "Definitions of non-default routing rule specified on a per\nnet basis. Constraints are entered as a (width,space) tuples\nspecified in microns.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: non-default routing rule",
                  "signature": null,
                  "switch": "-asic_ndr 'netname <(float,float)>",
                  "type": "(float,float)",
                  "value": null
              }
          },
          "pdk": {
              "defvalue": null,
              "example": [
                  "cli: -asic_pdk freepdk45",
                  "api: chip.set('asic','pdk','freepdk45')"
              ],
              "help": "Target ASIC PDK to use in the design.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC PDK target",
              "signature": null,
              "switch": "-asic_pdk <str>",
              "type": "str",
              "value": null
          },
          "pgmetal": {
              "defvalue": null,
              "example": [
                  "cli: -asic_pgmetal m1",
                  "api: chip.set('asic','pgmetal','m1')"
              ],
              "help": "Top metal layer used for power and ground routing within the\nlibrary. The parameter can be used to guide cell power grid\nhookup by APR tools.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: powergrid layer",
              "signature": null,
              "switch": "-asic_pgmetal '<str>'",
              "type": "str",
              "value": null
          },
          "rclayer": {
              "default": {
                  "defvalue": null,
                  "example": [
                      "cli: -asic_rclayer 'clk m3'",
                      "api: chip.set('asic', 'rclayer', 'clk', 'm3')"
                  ],
                  "help": "Technology agnostic metal layer to be used for parasitic\nextraction estimation during APR for the wire type specified\nCurrent the supported wire types are: clk, data. The metal\nlayers can be specified as technology agnostic SC layers\nstarting with m1 or as hard PDK specific layer names.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: parasitics layer",
                  "signature": null,
                  "switch": "-asic_rclayer 'sigtype <str>'",
                  "type": "str",
                  "value": null
              }
          },
          "stackup": {
              "defvalue": null,
              "example": [
                  "cli: -asic_stackup 2MA4MB2MC",
                  "api: chip.set('asic','stackup','2MA4MB2MC')"
              ],
              "help": "Target ASIC stackup to use in the design. The stackup is required\nparameter for PDKs with multiple metal stackups.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC Stackup target",
              "signature": null,
              "switch": "-asic_stackup <str>",
              "type": "str",
              "value": null
          },
          "var": {
              "default": {
                  "default": {
                      "defvalue": [],
                      "example": [
                          "cli: -asic_variable 'xyce modeltype bsim4'",
                          "api: chip.set('asic','var','xyce','modeltype','bsim4')"
                      ],
                      "help": "List of key/value strings specified on a per basis. The parameter\nshould only be used for specifying variables that are\nnot directly supported by the SiliconCompiler PDK schema.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "ASIC: special variable",
                      "signature": [],
                      "switch": "-asic_variable 'tool key <str>'",
                      "type": "[str]",
                      "value": []
                  }
              }
          },
          "vpinlayer": {
              "defvalue": null,
              "example": [
                  "cli: -asic_vpinlayer m3",
                  "api: chip.set('asic', 'vpinlayer', 'm3')"
              ],
              "help": "Metal layer to use for automated vertical pin placement\nduring APR.  The metal layers can be specified as technology\nagnostic SC layers starting with m1 or as hard PDK specific\nlayer names.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: vertical pin layer",
              "signature": null,
              "switch": "-asic_vpinlayer <str>",
              "type": "str",
              "value": null
          }
      },
      "checklist": {
          "default": {
              "default": {
                  "criteria": {
                      "defvalue": [],
                      "example": [
                          "cli: -checklist_criteria 'ISO D000 errors==0'",
                          "api: chip.set('checklist','ISO','D000','criteria','errors==0')"
                      ],
                      "help": "Simple list of signoff criteria for checklist item which\nmust all be met for signoff. Each signoff criteria consists of\na metric, a relational operator, and a value in the form.\n'metric op value'.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item criteria",
                      "signature": [],
                      "switch": "-checklist_criteria 'standard item <float>'",
                      "type": "[str]",
                      "value": []
                  },
                  "dataformat": {
                      "defvalue": null,
                      "example": [
                          "cli: -checklist_dataformat 'ISO D000 dataformat README'",
                          "api: chip.set('checklist','ISO','D000','dataformat','README')"
                      ],
                      "help": "Free text description of the type of data files acceptable as\nchecklist signoff validation.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item data format",
                      "signature": null,
                      "switch": "-checklist_dataformat 'standard item <float>'",
                      "type": "str",
                      "value": null
                  },
                  "description": {
                      "defvalue": null,
                      "example": [
                          "cli: -checklist_description 'ISO D000 A-DESCRIPTION'",
                          "api: chip.set('checklist','ISO','D000','description','A-DESCRIPTION')"
                      ],
                      "help": "A short one line description of the checklist item.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item description",
                      "signature": null,
                      "switch": "-checklist_description 'standard item <str>",
                      "type": "str",
                      "value": null
                  },
                  "ok": {
                      "defvalue": "false",
                      "example": [
                          "cli: -checklist_ok 'ISO D000 true'",
                          "api: chip.set('checklist','ISO','D000','ok', True)"
                      ],
                      "help": "Boolean check mark for the checklist item. A value of\nTrue indicates a human has inspected the all item dictionary\nparameters check out.",
                      "lock": "false",
                      "notes": null,
                      "require": "all",
                      "scope": "global",
                      "shorthelp": "Checklist: item ok",
                      "signature": null,
                      "switch": "-checklist_ok 'standard item <str>'",
                      "type": "bool",
                      "value": "false"
                  },
                  "rationale": {
                      "defvalue": [],
                      "example": [
                          "cli: -checklist_rational 'ISO D000 reliability'",
                          "api: chip.set('checklist','ISO','D000','rationale','reliability')"
                      ],
                      "help": "Rationale for the the checklist item. Rationale should be a\nunique alphanumeric code used by the standard or a short one line\nor single word description.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item rational",
                      "signature": [],
                      "switch": "-checklist_rationale 'standard item <str>",
                      "type": "[str]",
                      "value": []
                  },
                  "report": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -checklist_report 'ISO D000 my.rpt'",
                          "api: chip.set('checklist','ISO','D000','report','my.rpt')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to report(s) of specified type documenting the successful\nvalidation of the checklist item.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item report",
                      "signature": [],
                      "switch": "-checklist_report 'standard item <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "requirement": {
                      "defvalue": null,
                      "example": [
                          "cli: -checklist_requirement 'ISO D000 DOCSTRING'",
                          "api: chip.set('checklist','ISO','D000','requirement','DOCSTRING')"
                      ],
                      "help": "A complete requirement description of the checklist item\nentered as a multi-line string.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item requirement",
                      "signature": null,
                      "switch": "-checklist_requirement 'standard item <str>",
                      "type": "str",
                      "value": null
                  },
                  "task": {
                      "defvalue": [],
                      "example": [
                          "cli: -checklist_task 'ISO D000 (job0,place,0)'",
                          "api: chip.set('checklist','ISO','D000','task',('job0','place','0'))"
                      ],
                      "help": "Flowgraph job and task used to verify the checklist item.\nThe parameter should be left empty for manual and for tool\nflows that bypass the SC infrastructure.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item task",
                      "signature": [],
                      "switch": "-checklist_task 'standard item <(str, str, str)>'",
                      "type": "[(str,str,str)]",
                      "value": []
                  },
                  "waiver": {
                      "default": {
                          "author": [],
                          "copy": "false",
                          "date": [],
                          "defvalue": [],
                          "example": [
                              "cli: -checklist_waiver 'ISO D000 bold my.txt'",
                              "api: chip.set('checklist','ISO','D000','waiver','hold', 'my.txt')"
                          ],
                          "filehash": [],
                          "hashalgo": "sha256",
                          "help": "Filepath to report(s) documenting waivers for the checklist\nitem specified on a per metric basis.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "global",
                          "shorthelp": "Checklist: item metric waivers",
                          "signature": [],
                          "switch": "-checklist_waiver 'standard item metric <file>'",
                          "type": "[file]",
                          "value": []
                      }
                  }
              }
          }
      },
      "constraint": {
          "default": {
              "check": {
                  "defvalue": [],
                  "example": [
                      "cli: -constraint_check 'worst check setup'",
                      "api: chip.add('constraint','worst','check','setup')"
                  ],
                  "help": "List of checks for to perform for the scenario. The checks must\nalign with the capabilities of the EDA tools and flow being used.\nChecks generally include objectives like meeting setup and hold goals\nand minimize power. Standard check names include setup, hold, power,\nnoise, reliability.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint checks to perform",
                  "signature": [],
                  "switch": "-constraint_check 'scenario <str>'",
                  "type": "[str]",
                  "value": []
              },
              "file": {
                  "author": [],
                  "copy": "true",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -constraint_file 'worst hello.sdc'",
                      "api: chip.set('constraint','worst','file', 'hello.sdc')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "List of timing constraint files to use for the scenario. The\nvalues are combined with any constraints specified by the design\n'constraint' parameter. If no constraints are found, a default\nconstraint file is used based on the clock definitions.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint files",
                  "signature": [],
                  "switch": "-constraint_file 'scenario <file>'",
                  "type": "[file]",
                  "value": []
              },
              "libcorner": {
                  "defvalue": null,
                  "example": [
                      "cli: -constraint_libcorner 'worst ttt'",
                      "api: chip.set('constraint', 'worst', 'libcorner', 'ttt')"
                  ],
                  "help": "Library corner applied to the scenario to scale\nlibrary timing models based on the libcorner value for models\nthat support it. The parameter is ignored for libraries that\nhave one hard coded model per libcorner.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint library corner",
                  "signature": null,
                  "switch": "-constraint_libcorner 'scenario <str>'",
                  "type": "str",
                  "value": null
              },
              "mode": {
                  "defvalue": null,
                  "example": [
                      "cli: -constraint_mode 'worst test'",
                      "api: chip.set('constraint',  'worst','mode', 'test')"
                  ],
                  "help": "Operating mode for the scenario. Operating mode strings\ncan be values such as test, functional, standby.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint operating mode",
                  "signature": null,
                  "switch": "-constraint_mode 'scenario <str>'",
                  "type": "str",
                  "value": null
              },
              "opcond": {
                  "defvalue": null,
                  "example": [
                      "cli: -constraint_opcond 'worst typical_1.0'",
                      "api: chip.set('constraint', 'worst', 'opcond',  'typical_1.0')"
                  ],
                  "help": "Operating condition applied to the scenario. The value\ncan be used to access specific conditions within the library\ntiming models from the 'logiclib' timing models.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint operating condition",
                  "signature": null,
                  "switch": "-constraint_opcond 'scenario <str>'",
                  "type": "str",
                  "value": null
              },
              "pexcorner": {
                  "defvalue": null,
                  "example": [
                      "cli: -constraint_pexcorner 'worst max'",
                      "api: chip.set('constraint', 'worst', 'pexcorner', 'max')"
                  ],
                  "help": "Parasitic corner applied to the scenario. The\n'pexcorner' string must match a corner found in the pdk\npexmodel setup.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint pex corner",
                  "signature": null,
                  "switch": "-constraint_pexcorner 'scenario <str>'",
                  "type": "str",
                  "value": null
              },
              "temperature": {
                  "defvalue": null,
                  "example": [
                      "cli: -constraint_temperature 'worst 125'",
                      "api: chip.set('constraint', 'worst', 'temperature','125')"
                  ],
                  "help": "Chip temperature applied to the scenario specified in degrees C.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint temperature",
                  "signature": null,
                  "switch": "-constraint_temperature 'scenario <float>'",
                  "type": "float",
                  "value": null
              },
              "voltage": {
                  "defvalue": null,
                  "example": [
                      "cli: -constraint_voltage 'worst 0.9'",
                      "api: chip.set('constraint', 'worst','voltage', '0.9')"
                  ],
                  "help": "Operating voltage applied to the scenario.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint voltage level",
                  "signature": null,
                  "switch": "-constraint_voltage 'scenario <float>'",
                  "type": "float",
                  "unit": "V",
                  "value": null
              }
          }
      },
      "datasheet": {
          "default": {
              "feature": {
                  "default": {
                      "defvalue": null,
                      "example": [
                          "cli: -datasheet_feature 'mydevice ram 64e6'",
                          "api: chip.set('datasheet','mydevice','feature','ram', 1e9)"
                      ],
                      "help": "Quantity of a specified feature. The 'unit'\nfield should be used to specify the units used when unclear.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Datasheet: device feature specification",
                      "signature": null,
                      "switch": "-datasheet_feature 'design name <float>'",
                      "type": "float",
                      "value": null
                  }
              },
              "limits": {
                  "junctiontemp": {
                      "defvalue": null,
                      "example": [
                          "cli: -datasheet_junctiontemp 'mydevice (-40,125)'",
                          "api: chip.set('datasheet','mydevice','limits','junctiontemp',(-40,125))"
                      ],
                      "help": "Device absolute junction temperature limits not to be exceeded.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Datasheet: absolute junction temperature limits",
                      "signature": null,
                      "switch": "-datasheet_junctiontemp 'design <(float,float)>'",
                      "type": "(float,float)",
                      "value": null
                  },
                  "storagetemp": {
                      "defvalue": null,
                      "example": [
                          "cli: -datasheet_storagetemp 'mydevice (-40,125)'",
                          "api: chip.set('datasheet','mydevice','limits','storagetemp',(-40,125))"
                      ],
                      "help": "Device absolute storage temperature limits not to be exceeded.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Datasheet: absolute storage temperature limits",
                      "signature": null,
                      "switch": "-datasheet_storagetemp 'design <(float,float)>'",
                      "type": "(float,float)",
                      "value": null
                  },
                  "voltage": {
                      "default": {
                          "defvalue": null,
                          "example": [
                              "cli: -datasheet_limits_voltage 'mydevice vdd (-0.4,1.1)'",
                              "api: chip.set('datasheet','mydevice','limits','voltage','vdd', (-0.4,1.1))"
                          ],
                          "help": "Device absolute minimum/maximum voltage not to be\nexceeded, specified on a per pin basis.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Datasheet: absolute voltage limits",
                          "signature": null,
                          "switch": "-datasheet_limits_voltage 'design pin <(float,float)>'",
                          "type": "(float,float)",
                          "value": null
                      }
                  }
              },
              "pin": {
                  "default": {
                      "capacitance": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_capacitance 'mydevice sclk global (1e-12, 1.2e-12, 1.5e-12)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','capacitance','global',(1e-12, 1.2e-12, 1.5e-12)"
                              ],
                              "help": "Pin capacitance. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: capacitance",
                              "signature": null,
                              "switch": "-datasheet_pin_capacitance 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "F",
                              "value": null
                          }
                      },
                      "clk": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_clk 'mydevice ina global clka'",
                                  "api: chip.set('datasheet','mydevice','pin','ina','clk','global','clka')"
                              ],
                              "help": "Pin related clock specified on a per mode basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin related clock",
                              "signature": null,
                              "switch": "-datasheet_pin_clk 'design name mode <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "complement": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_complement 'mydevice ina global inb'",
                                  "api: chip.set('datasheet','mydevice','pin','ina','complement','global','inb')"
                              ],
                              "help": "Pin complement specified on a per mode basis for differential\nsignals.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin complement",
                              "signature": null,
                              "switch": "-datasheet_pin_complement 'design name mode <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "dir": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_dir 'mydevice clk global input'",
                                  "api: chip.set('datasheet','mydevice','pin','clk','dir','global','input')"
                              ],
                              "help": "Pin direction specified on a per mode basis. Acceptable pin\ndirections include: input, output, inout.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin direction",
                              "signature": null,
                              "switch": "-datasheet_pin_dir 'design name mode <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "dutycycle": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_dutycycle 'mydevice sclk global (45, 50, 55)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','dutycycle','global',(45, 50, 55)"
                              ],
                              "help": "Pin duty cycle. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: duty cycle",
                              "signature": null,
                              "switch": "-datasheet_pin_dutycycle 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "%",
                              "value": null
                          }
                      },
                      "ground": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_ground 'mydevice ina ground vss'",
                                  "api: chip.set('datasheet','mydevice','pin','ina','ground','global','vss')"
                              ],
                              "help": "Pin related ground rail specified on a per mode basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin related ground",
                              "signature": null,
                              "switch": "-datasheet_pin_ground 'design name mode <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "idrive": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_idrive 'mydevice sclk global (0.01, 0.012, 0.015)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','idrive','global',(0.01, 0.012, 0.015)"
                              ],
                              "help": "Pin drive current. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: drive current",
                              "signature": null,
                              "switch": "-datasheet_pin_idrive 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "A",
                              "value": null
                          }
                      },
                      "iinject": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_iinject 'mydevice sclk global (0.001, 0.0012, 0.0015)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','iinject','global',(0.001, 0.0012, 0.0015)"
                              ],
                              "help": "Pin injection current. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: injection current",
                              "signature": null,
                              "switch": "-datasheet_pin_iinject 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "A",
                              "value": null
                          }
                      },
                      "ileakage": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_ileakage 'mydevice sclk global (1e-06, 1.2e-06, 1.5e-06)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','ileakage','global',(1e-06, 1.2e-06, 1.5e-06)"
                              ],
                              "help": "Pin leakage current. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: leakage current",
                              "signature": null,
                              "switch": "-datasheet_pin_ileakage 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "A",
                              "value": null
                          }
                      },
                      "map": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_map 'mydevice in0 bga512 B4'",
                                  "api: chip.set('datasheet','mydevice','pin','in0','map','bga512','B4')"
                              ],
                              "help": "Signal to package pin mapping specified on a per package basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: package pin map",
                              "signature": null,
                              "switch": "-datasheet_pin_map 'design name package <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "rdiff": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_rdiff 'mydevice sclk global (45, 50, 55)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','rdiff','global',(45, 50, 55)"
                              ],
                              "help": "Pin differential pair resistance. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: differential pair resistance",
                              "signature": null,
                              "switch": "-datasheet_pin_rdiff 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "ohm",
                              "value": null
                          }
                      },
                      "resetvalue": {
                          "default": {
                              "defvalue": [],
                              "example": [
                                  "cli: -datasheet_pin_resetvalue 'mydevice clk global weak1'",
                                  "api: chip.set('datasheet','mydevice','pin','clk','resetvalue','global','weak1')"
                              ],
                              "help": "Pin reset value specified on a per mode basis. Legal reset\nvalues include weak1, weak0, strong0, strong1, highz.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin reset value",
                              "signature": [],
                              "switch": "-datasheet_pin_resetvalue 'design name mode <str>'",
                              "type": "[str]",
                              "value": []
                          }
                      },
                      "rpulldown": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_rpulldown 'mydevice sclk global (1000, 1200, 3000)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','rpulldown','global',(1000, 1200, 3000)"
                              ],
                              "help": "Pin pulldown resistance. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pulldown resistance",
                              "signature": null,
                              "switch": "-datasheet_pin_rpulldown 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "ohm",
                              "value": null
                          }
                      },
                      "rpullup": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_rpullup 'mydevice sclk global (1000, 1200, 3000)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','rpullup','global',(1000, 1200, 3000)"
                              ],
                              "help": "Pin pullup resistance. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pullup resistance",
                              "signature": null,
                              "switch": "-datasheet_pin_rpullup 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "ohm",
                              "value": null
                          }
                      },
                      "standard": {
                          "default": {
                              "defvalue": [],
                              "example": [
                                  "cli: -datasheet_pin_standard 'mydevice ba0 global ddr4'",
                                  "api: chip.set('datasheet','mydevice','pin','ina','standard','global','ddr4')"
                              ],
                              "help": "Pin communication standard specified on a per mode basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin standard",
                              "signature": [],
                              "switch": "-datasheet_pin_standard 'design name mode <str>'",
                              "type": "[str]",
                              "value": []
                          }
                      },
                      "supply": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_supply 'mydevice ina global vdd'",
                                  "api: chip.set('datasheet','mydevice','pin','ina','supply','global','vdd')"
                              ],
                              "help": "Pin related power supply specified on a per mode basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin related power supply",
                              "signature": null,
                              "switch": "-datasheet_pin_supply 'design name mode <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "tfall": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_tfall 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','tfall','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin fall transition. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: fall transition",
                              "signature": null,
                              "switch": "-datasheet_pin_tfall 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "thold": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_thold 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','thold','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin hold time. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: hold time",
                              "signature": null,
                              "switch": "-datasheet_pin_thold 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "tjitter": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_tjitter 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','tjitter','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin rms jitter. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: rms jitter",
                              "signature": null,
                              "switch": "-datasheet_pin_tjitter 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "tperiod": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_tperiod 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','tperiod','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin minimum period. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: minimum period",
                              "signature": null,
                              "switch": "-datasheet_pin_tperiod 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "tpulse": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_tpulse 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','tpulse','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin pulse width. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pulse width",
                              "signature": null,
                              "switch": "-datasheet_pin_tpulse 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "trise": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_trise 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','trise','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin rise transition. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: rise transition",
                              "signature": null,
                              "switch": "-datasheet_pin_trise 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "tsetup": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_tsetup 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','tsetup','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin setup time. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: setup time",
                              "signature": null,
                              "switch": "-datasheet_pin_tsetup 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "type": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_type 'mydevice vdd type power'",
                                  "api: chip.set('datasheet','mydevice','pin','vdd','type','global','power')"
                              ],
                              "help": "Pin type specified on a per mode basis. Acceptable pin types\ninclude: digital, analog, clk, power, ground",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin type",
                              "signature": null,
                              "switch": "-datasheet_pin_type 'design name mode <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "vcdm": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vcdm 'mydevice sclk global (100, 300, 500)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vcdm','global',(100, 300, 500)"
                              ],
                              "help": "Pin human body model ESD tolerance. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: human body model ESD tolerance",
                              "signature": null,
                              "switch": "-datasheet_pin_vcdm 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vcm": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vcm 'mydevice sclk global (0.3, 1.2, 1.6)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vcm','global',(0.3, 1.2, 1.6)"
                              ],
                              "help": "Pin common mode voltage. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: common mode voltage",
                              "signature": null,
                              "switch": "-datasheet_pin_vcm 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vdiff": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vdiff 'mydevice sclk global (0.2, 0.3, 0.9)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vdiff','global',(0.2, 0.3, 0.9)"
                              ],
                              "help": "Pin differential voltage. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: differential voltage",
                              "signature": null,
                              "switch": "-datasheet_pin_vdiff 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vhbm": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vhbm 'mydevice sclk global (100, 300, 500)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vhbm','global',(100, 300, 500)"
                              ],
                              "help": "Pin machine model ESD tolerance. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: machine model ESD tolerance",
                              "signature": null,
                              "switch": "-datasheet_pin_vhbm 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vih": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vih 'mydevice sclk global (1.4, 1.8, 2.2)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vih','global',(1.4, 1.8, 2.2)"
                              ],
                              "help": "Pin high input voltage level. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: high input voltage level",
                              "signature": null,
                              "switch": "-datasheet_pin_vih 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vil": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vil 'mydevice sclk global (-0.2, 0, 1.0)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vil','global',(-0.2, 0, 1.0)"
                              ],
                              "help": "Pin low input voltage level. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: low input voltage level",
                              "signature": null,
                              "switch": "-datasheet_pin_vil 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vnoise": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vnoise 'mydevice sclk global (0, 0.01, 0.1)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vnoise','global',(0, 0.01, 0.1)"
                              ],
                              "help": "Pin random voltage noise. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: random voltage noise",
                              "signature": null,
                              "switch": "-datasheet_pin_vnoise 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "voh": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_voh 'mydevice sclk global (4.6, 4.8, 5.2)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','voh','global',(4.6, 4.8, 5.2)"
                              ],
                              "help": "Pin high output voltage level. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: high output voltage level",
                              "signature": null,
                              "switch": "-datasheet_pin_voh 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vol": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vol 'mydevice sclk global (-0.2, 0, 0.2)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vol','global',(-0.2, 0, 0.2)"
                              ],
                              "help": "Pin low output voltage level. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: low output voltage level",
                              "signature": null,
                              "switch": "-datasheet_pin_vol 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      }
                  }
              }
          }
      },
      "design": {
          "defvalue": null,
          "example": [
              "cli: -design hello_world",
              "api: chip.set('design', 'hello_world')"
          ],
          "help": "Name of the top level module or library. Required for all\nchip objects.",
          "lock": "false",
          "notes": null,
          "require": "all",
          "scope": "global",
          "shorthelp": "Design top module name",
          "signature": null,
          "switch": "-design <str>",
          "type": "str",
          "value": null
      },
      "flowgraph": {
          "default": {
              "default": {
                  "default": {
                      "args": {
                          "defvalue": [],
                          "example": [
                              "cli: -flowgraph_args 'asicflow cts 0 0'",
                              "api:  chip.add('flowgraph','asicflow','cts','0','args','0')"
                          ],
                          "help": "User specified flowgraph string arguments specified on a per\nstep and per index basis.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Flowgraph: setup arguments",
                          "signature": [],
                          "switch": "-flowgraph_args 'flow step index <str>'",
                          "type": "[str]",
                          "value": []
                      },
                      "goal": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -flowgraph_goal 'asicflow cts 0 area_cells 1.0'",
                                  "api:  chip.set('flowgraph','asicflow','cts','0','goal','errors', 0)"
                              ],
                              "help": "Goals specified on a per step and per metric basis used to\ndetermine whether a certain task can be considered when merging\nmultiple tasks at a minimum or maximum node. A task is considered\nfailing if the absolute value of any of its metrics are larger than\nthe goal for that metric, if set.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Flowgraph: metric goals",
                              "signature": null,
                              "switch": "-flowgraph_goal 'flow step index metric <float>'",
                              "type": "float",
                              "value": null
                          }
                      },
                      "input": {
                          "defvalue": [],
                          "example": [
                              "cli: -flowgraph_input 'asicflow cts 0 (place,0)'",
                              "api:  chip.set('flowgraph','asicflow','cts','0','input',('place','0'))"
                          ],
                          "help": "A list of inputs for the current step and index, specified as a\n(step,index) tuple.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Flowgraph: step input",
                          "signature": [],
                          "switch": "-flowgraph_input 'flow step index <(str,str)>'",
                          "type": "[(str,str)]",
                          "value": []
                      },
                      "select": {
                          "defvalue": [],
                          "example": [
                              "cli: -flowgraph_select 'asicflow cts 0 (place,42)'",
                              "api:  chip.set('flowgraph','asicflow', 'cts','0','select',('place','42'))"
                          ],
                          "help": "List of selected inputs for the current step/index specified as\n(in_step,in_index) tuple.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Flowgraph: task select record",
                          "signature": [],
                          "switch": "-flowgraph_select 'flow step index <(str,str)>'",
                          "type": "[(str,str)]",
                          "value": []
                      },
                      "status": {
                          "defvalue": null,
                          "example": [
                              "cli: -flowgraph_status 'asicflow cts 10 success'",
                              "api:  chip.set('flowgraph','asicflow', 'cts','10','status', 'success')"
                          ],
                          "help": "Parameter that tracks the status of a task. Valid values are:\n\n* \"success\": task ran successfully\n* \"error\": task failed with an error\n\nAn empty value indicates the task has not yet been completed.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Flowgraph: task status",
                          "signature": null,
                          "switch": "-flowgraph_status 'flow step index <str>'",
                          "type": "str",
                          "value": null
                      },
                      "timeout": {
                          "defvalue": null,
                          "example": [
                              "cli: -flowgraph_timeout 'asicflow cts 0 3600'",
                              "api:  chip.set('flowgraph','asicflow','cts','0','timeout', 3600)"
                          ],
                          "help": "Timeout value in seconds specified on a per step and per index\nbasis. The flowgraph timeout value is compared against the\nwall time tracked by the SC runtime to determine if an\noperation should continue. Timeout values help in situations\nwhere 1.) an operation is stuck and may never finish. 2.) the\noperation progress has saturated and continued execution has\na negative return on investment.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Flowgraph: task timeout value",
                          "signature": null,
                          "switch": "-flowgraph_timeout 'flow step 0 <float>'",
                          "type": "float",
                          "unit": "s",
                          "value": null
                      },
                      "tool": {
                          "defvalue": null,
                          "example": [
                              "cli: -flowgraph_tool 'asicflow place 0 openroad'",
                              "api: chip.set('flowgraph','asicflow','place','0','tool','openroad')"
                          ],
                          "help": "Name of the tool name used for task execution. Builtin tool names\nassociated bound to core API functions include: minimum, maximum, join,\nverify, mux.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Flowgraph: tool selection",
                          "signature": null,
                          "switch": "-flowgraph_tool 'flow step <str>'",
                          "type": "str",
                          "value": null
                      },
                      "valid": {
                          "defvalue": "false",
                          "example": [
                              "cli: -flowgraph_valid 'asicflow cts 0 true'",
                              "api:  chip.set('flowgraph','asicflow','cts','0','valid',True)"
                          ],
                          "help": "Flowgraph valid bit specified on a per step and per index basis.\nThe parameter can be used to control flow execution. If the bit\nis cleared (0), then the step/index combination is invalid and\nshould not be run.",
                          "lock": "false",
                          "notes": null,
                          "require": "all",
                          "scope": "job",
                          "shorthelp": "Flowgraph: task valid bit",
                          "signature": null,
                          "switch": "-flowgraph_valid 'flow step index <str>'",
                          "type": "bool",
                          "value": "false"
                      },
                      "weight": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -flowgraph_weight 'asicflow cts 0 area_cells 1.0'",
                                  "api:  chip.set('flowgraph','asicflow','cts','0','weight','area_cells',1.0)"
                              ],
                              "help": "Weights specified on a per step and per metric basis used to give\neffective \"goodnes\" score for a step by calculating the sum all step\nreal metrics results by the corresponding per step weights.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Flowgraph: metric weights",
                              "signature": null,
                              "switch": "-flowgraph_weight 'flow step index metric <float>'",
                              "type": "float",
                              "value": null
                          }
                      }
                  }
              }
          }
      },
      "fpga": {
          "arch": {
              "author": [],
              "copy": "true",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -fpga_arch myfpga.xml",
                  "api:  chip.set('fpga', 'arch', 'myfpga.xml')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "Architecture definition file for FPGA place and route\ntool. For the VPR tool, the file is a required XML based description,\nallowing targeting a large number of virtual and commercial\narchitectures. For most commercial tools, the fpga part name provides\nenough information to enable compilation and the 'arch' parameter is\noptional.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "FPGA: architecture file",
              "signature": [],
              "switch": "-fpga_arch <file>",
              "type": "[file]",
              "value": []
          },
          "board": {
              "defvalue": null,
              "example": [
                  "cli: -fpga_board parallella",
                  "api:  chip.set('fpga', 'board', 'parallella')"
              ],
              "help": "Complete board name used as a device target by the FPGA compilation\ntool. The board name must be an exact string match to the partname\nhard coded within the FPGA eda tool. The parameter is optional and can\nbe used in place of a partname and pin constraints for some tools.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "FPGA: board name",
              "signature": null,
              "switch": "-fpga_board <str>",
              "type": "str",
              "value": null
          },
          "flash": {
              "defvalue": "false",
              "example": [
                  "cli: -fpga_flash",
                  "api:  chip.set('fpga', 'flash', True)"
              ],
              "help": "Specifies that the bitstream should be flashed in the board/device.\nThe default is to load the bitstream into volatile memory (SRAM).",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "FPGA: flash enable",
              "signature": null,
              "switch": "-fpga_flash <bool>",
              "type": "bool",
              "value": "false"
          },
          "partname": {
              "defvalue": null,
              "example": [
                  "cli: -fpga_partname fpga64k",
                  "api:  chip.set('fpga', 'partname', 'fpga64k')"
              ],
              "help": "Complete part name used as a device target by the FPGA compilation\ntool. The part name must be an exact string match to the partname\nhard coded within the FPGA eda tool.",
              "lock": "false",
              "notes": null,
              "require": "fpga",
              "scope": "job",
              "shorthelp": "FPGA: part name",
              "signature": null,
              "switch": "-fpga_partname <str>",
              "type": "str",
              "value": null
          },
          "program": {
              "defvalue": "false",
              "example": [
                  "cli: -fpga_program",
                  "api:  chip.set('fpga', 'program', True)"
              ],
              "help": "Specifies that the bitstream should be loaded into an FPGA.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "FPGA: program enable",
              "signature": null,
              "switch": "-fpga_program <bool>",
              "type": "bool",
              "value": "false"
          },
          "vendor": {
              "defvalue": null,
              "example": [
                  "cli: -fpga_vendor acme",
                  "api:  chip.set('fpga', 'vendor', 'acme')"
              ],
              "help": "Name of the FPGA vendor. The parameter is used to check part\nname and to select the eda tool flow in case 'edaflow' is\nunspecified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "FPGA: vendor name",
              "signature": null,
              "switch": "-fpga_vendor <str>",
              "type": "str",
              "value": null
          }
      },
      "history": {},
      "input": {
          "default": {
              "author": [],
              "copy": "true",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -input 'verilog hello_world.v'",
                  "api: chip.set(input,'verilog','hello_world.v')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "List of input files specifed by type. The filetype name must\nalign with the parameter names within the flow and tool setup\nscripts. Examples of acceptable file types include python, c,\nsystemc, verilog, vhdl, netlist, def, gds, gerber, saif, sdc,\nsaif, vcd, spef, sdf.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Input files",
              "signature": [],
              "switch": "-input 'filetype <file>'",
              "type": "[file]",
              "value": []
          }
      },
      "library": {},
      "metric": {
          "default": {
              "default": {
                  "averagepower": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_averagepower 'place 0 0.01'",
                          "api: chip.set('metric','place','0','averagepower', 0.01)"
                      ],
                      "help": "Metric tracking the average workload power of the design specified on a per step\nand index basis. Power metric depend heavily on the method\nbeing used for extraction: dynamic vs static, workload\nspecification (vcd vs saif), power models, process/voltage/temperature.\nThe power averagepower metric tries to capture the data that would\nusually be reflected inside a datasheet given the approprate\nfootnote conditions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: averagepower",
                      "signature": null,
                      "switch": "-metric_averagepower 'step index <float>'",
                      "type": "float",
                      "unit": "mw",
                      "value": null
                  },
                  "brams": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_brams 'place 0 100'",
                          "api: chip.set('metric','place','0','brams', 100)"
                      ],
                      "help": "Metric tracking the total FPGA BRAM tiles used by the design as reported\nby the implementation tool. There is no standardized definition\nfor this metric across vendors, so metric comparisons can\ngenerally only be done between runs on identical tools and\ndevice families.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: FPGA BRAM tiles",
                      "signature": null,
                      "switch": "-metric_brams 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "buffers": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_buffers 'place 0 100'",
                          "api: chip.set('metric','place','0','buffers', 50)"
                      ],
                      "help": "Metric tracking the total number of buffer and inverter instances in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: buffers",
                      "signature": null,
                      "switch": "-metric_buffers 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "cellarea": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_cellarea 'place 0 100.00'",
                          "api: chip.set('metric','place','0','cellarea', 100.00)"
                      ],
                      "help": "Metric tracking the total cell area (ignoring fillers) occupied by the design.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: cellarea",
                      "signature": null,
                      "switch": "-metric_cellarea 'step index <float>'",
                      "type": "float",
                      "unit": "um^2",
                      "value": null
                  },
                  "cells": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_cells 'place 0 100'",
                          "api: chip.set('metric','place','0','cells', 50)"
                      ],
                      "help": "Metric tracking the total number of cell instances in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: cells",
                      "signature": null,
                      "switch": "-metric_cells 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "coverage": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_coverage 'place 0 99.9'",
                          "api: chip.set('metric','place','0','coverage', 99.9)"
                      ],
                      "help": "Metric tracking the test coverage in the design expressed as a percentage\nwith 100 meaning full coverage. The meaning of the metric depends on the\ntask being executed. It can refer to code coverage, feature coverage,\nstuck at fault coverage.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: coverage",
                      "signature": null,
                      "switch": "-metric_coverage 'step index <float>'",
                      "type": "float",
                      "unit": "%",
                      "value": null
                  },
                  "dozepower": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_dozepower 'place 0 0.01'",
                          "api: chip.set('metric','place','0','dozepower', 0.01)"
                      ],
                      "help": "Metric tracking the power consumed while in low frequency operating mode of the design specified on a per step\nand index basis. Power metric depend heavily on the method\nbeing used for extraction: dynamic vs static, workload\nspecification (vcd vs saif), power models, process/voltage/temperature.\nThe power dozepower metric tries to capture the data that would\nusually be reflected inside a datasheet given the approprate\nfootnote conditions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: dozepower",
                      "signature": null,
                      "switch": "-metric_dozepower 'step index <float>'",
                      "type": "float",
                      "unit": "mw",
                      "value": null
                  },
                  "drvs": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_drvs 'dfm 0 0'",
                          "api: chip.set('metric','dfm','0','drvs', 0)"
                      ],
                      "help": "Metric tracking the total number of design rule violations on a\nper step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: total drvs",
                      "signature": null,
                      "switch": "-metric_drvs 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "dsps": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_dsps 'place 0 100'",
                          "api: chip.set('metric','place','0','dsps', 100)"
                      ],
                      "help": "Metric tracking the total FPGA DSP slices used by the design as reported\nby the implementation tool. There is no standardized definition\nfor this metric across vendors, so metric comparisons can\ngenerally only be done between runs on identical tools and\ndevice families.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: FPGA DSP slices",
                      "signature": null,
                      "switch": "-metric_dsps 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "errors": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_errors 'dfm 0 0'",
                          "api: chip.set('metric','dfm','0','errors', 0)"
                      ],
                      "help": "Metric tracking the total number of errors on a\nper step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: total errors",
                      "signature": null,
                      "switch": "-metric_errors 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "exetime": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_exetime 'dfm 0 10.0'",
                          "api: chip.set('metric','dfm','0','exetime', 10.0)"
                      ],
                      "help": "Metric tracking time spent by the eda executable 'exe' on a\nper step and index basis. It does not include the siliconcompiler\nruntime overhead or time waitig for I/O operations and\ninter-processor communication to complete.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: exetime",
                      "signature": null,
                      "switch": "-metric_exetime 'step index <float>'",
                      "type": "float",
                      "unit": "s",
                      "value": null
                  },
                  "holdpaths": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_holdpaths 'place 0 10'",
                          "api: chip.set('metric','place','0','holdpaths', 10)"
                      ],
                      "help": "Metric tracking the total number of timing paths violating hold\nconstraints.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: holdpaths",
                      "signature": null,
                      "switch": "-metric_holdpaths 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "holdslack": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_holdslack 'place 0 0.01'",
                          "api: chip.set('metric','place','0','holdslack', 0.01)"
                      ],
                      "help": "Metric tracking the worst hold slack (positive or negative) on a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: holdslack",
                      "signature": null,
                      "switch": "-metric_holdslack 'step index <float>'",
                      "type": "float",
                      "unit": "ns",
                      "value": null
                  },
                  "holdtns": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_holdtns 'place 0 0.01'",
                          "api: chip.set('metric','place','0','holdtns', 0.01)"
                      ],
                      "help": "Metric tracking the total negative hold slack (TNS) on a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: holdtns",
                      "signature": null,
                      "switch": "-metric_holdtns 'step index <float>'",
                      "type": "float",
                      "unit": "ns",
                      "value": null
                  },
                  "holdwns": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_holdwns 'place 0 0.01'",
                          "api: chip.set('metric','place','0','holdwns', 0.01)"
                      ],
                      "help": "Metric tracking the worst negative hold slack (positive values truncated to zero) on a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: holdwns",
                      "signature": null,
                      "switch": "-metric_holdwns 'step index <float>'",
                      "type": "float",
                      "unit": "ns",
                      "value": null
                  },
                  "idlepower": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_idlepower 'place 0 0.01'",
                          "api: chip.set('metric','place','0','idlepower', 0.01)"
                      ],
                      "help": "Metric tracking the power while not performing useful work of the design specified on a per step\nand index basis. Power metric depend heavily on the method\nbeing used for extraction: dynamic vs static, workload\nspecification (vcd vs saif), power models, process/voltage/temperature.\nThe power idlepower metric tries to capture the data that would\nusually be reflected inside a datasheet given the approprate\nfootnote conditions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: idlepower",
                      "signature": null,
                      "switch": "-metric_idlepower 'step index <float>'",
                      "type": "float",
                      "unit": "mw",
                      "value": null
                  },
                  "irdrop": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_irdrop 'place 0 0.05'",
                          "api: chip.set('metric','place','0','irdrop', 0.05)"
                      ],
                      "help": "Metric tracking the peak IR drop in the design based on extracted\npower and ground rail parasitics, library power models, and\nswitching activity. The switching activity calculated on a per\nnode basis is taken from one of three possible sources, in order\nof priority: VCD file, SAIF file, 'activityfactor' parameter.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: peak IR drop",
                      "signature": null,
                      "switch": "-metric_irdrop 'step index <float>'",
                      "type": "float",
                      "unit": "mv",
                      "value": null
                  },
                  "leakagepower": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_leakagepower 'place 0 0.01'",
                          "api: chip.set('metric','place','0','leakagepower', 0.01)"
                      ],
                      "help": "Metric tracking the leakage power with rails active but without any dynamic switching activity of the design specified on a per step\nand index basis. Power metric depend heavily on the method\nbeing used for extraction: dynamic vs static, workload\nspecification (vcd vs saif), power models, process/voltage/temperature.\nThe power leakagepower metric tries to capture the data that would\nusually be reflected inside a datasheet given the approprate\nfootnote conditions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: leakagepower",
                      "signature": null,
                      "switch": "-metric_leakagepower 'step index <float>'",
                      "type": "float",
                      "unit": "mw",
                      "value": null
                  },
                  "luts": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_luts 'place 0 100'",
                          "api: chip.set('metric','place','0','luts', 100)"
                      ],
                      "help": "Metric tracking the total FPGA LUTs used by the design as reported\nby the implementation tool. There is no standardized definition\nfor this metric across vendors, so metric comparisons can\ngenerally only be done between runs on identical tools and\ndevice families.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: FPGA LUTs",
                      "signature": null,
                      "switch": "-metric_luts 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "macros": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_macros 'place 0 100'",
                          "api: chip.set('metric','place','0','macros', 50)"
                      ],
                      "help": "Metric tracking the total number of macros in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: macros",
                      "signature": null,
                      "switch": "-metric_macros 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "memory": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_memory 'dfm 0 10e9'",
                          "api: chip.set('metric','dfm','0','memory', 10e9)"
                      ],
                      "help": "Metric tracking total peak program memory footprint on a per\nstep and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: memory",
                      "signature": null,
                      "switch": "-metric_memory 'step index <float>'",
                      "type": "float",
                      "unit": "B",
                      "value": null
                  },
                  "nets": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_nets 'place 0 100'",
                          "api: chip.set('metric','place','0','nets', 50)"
                      ],
                      "help": "Metric tracking the total number of nets in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: nets",
                      "signature": null,
                      "switch": "-metric_nets 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "overflow": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_overflow 'place 0 0'",
                          "api: chip.set('metric','place','0','overflow', 50)"
                      ],
                      "help": "Metric tracking the total number of overflow tracks for the routing\non per step and index basis. Any non-zero number suggests an over\ncongested design. To analyze where the congestion is occurring\ninspect the router log files for detailed per metal overflow\nreporting and open up the design to find routing hotspots.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: overflow",
                      "signature": null,
                      "switch": "-metric_overflow 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "peakpower": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_peakpower 'place 0 0.01'",
                          "api: chip.set('metric','place','0','peakpower', 0.01)"
                      ],
                      "help": "Metric tracking the worst case total peak power of the design specified on a per step\nand index basis. Power metric depend heavily on the method\nbeing used for extraction: dynamic vs static, workload\nspecification (vcd vs saif), power models, process/voltage/temperature.\nThe power peakpower metric tries to capture the data that would\nusually be reflected inside a datasheet given the approprate\nfootnote conditions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: peakpower",
                      "signature": null,
                      "switch": "-metric_peakpower 'step index <float>'",
                      "type": "float",
                      "unit": "mw",
                      "value": null
                  },
                  "pins": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_pins 'place 0 100'",
                          "api: chip.set('metric','place','0','pins', 50)"
                      ],
                      "help": "Metric tracking the total number of pins in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: pins",
                      "signature": null,
                      "switch": "-metric_pins 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "registers": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_registers 'place 0 100'",
                          "api: chip.set('metric','place','0','registers', 50)"
                      ],
                      "help": "Metric tracking the total number of register instances in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: registers",
                      "signature": null,
                      "switch": "-metric_registers 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "security": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_security 'place 0 100'",
                          "api: chip.set('metric','place','0','security', 100)"
                      ],
                      "help": "Metric tracking the level of security (1/vulnerability) of the design.\nA completely secure design would have a score of 100. There is no\nabsolute scale for the security metrics (like with power, area, etc)\nso the metric will be task and tool dependent.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: security",
                      "signature": null,
                      "switch": "-metric_security 'step index <float>'",
                      "type": "float",
                      "unit": "%",
                      "value": null
                  },
                  "setuppaths": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_setuppaths 'place 0 10'",
                          "api: chip.set('metric','place','0','setuppaths', 10)"
                      ],
                      "help": "Metric tracking the total number of timing paths violating setup\nconstraints.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: setuppaths",
                      "signature": null,
                      "switch": "-metric_setuppaths 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "setupslack": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_setupslack 'place 0 0.01'",
                          "api: chip.set('metric','place','0','setupslack', 0.01)"
                      ],
                      "help": "Metric tracking the worst setup slack (positive or negative) on a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: setupslack",
                      "signature": null,
                      "switch": "-metric_setupslack 'step index <float>'",
                      "type": "float",
                      "unit": "ns",
                      "value": null
                  },
                  "setuptns": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_setuptns 'place 0 0.01'",
                          "api: chip.set('metric','place','0','setuptns', 0.01)"
                      ],
                      "help": "Metric tracking the total negative setup slack (TNS) on a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: setuptns",
                      "signature": null,
                      "switch": "-metric_setuptns 'step index <float>'",
                      "type": "float",
                      "unit": "ns",
                      "value": null
                  },
                  "setupwns": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_setupwns 'place 0 0.01'",
                          "api: chip.set('metric','place','0','setupwns', 0.01)"
                      ],
                      "help": "Metric tracking the worst negative setup slack (positive values truncated to zero) on a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: setupwns",
                      "signature": null,
                      "switch": "-metric_setupwns 'step index <float>'",
                      "type": "float",
                      "unit": "ns",
                      "value": null
                  },
                  "sleeppower": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_sleeppower 'place 0 0.01'",
                          "api: chip.set('metric','place','0','sleeppower', 0.01)"
                      ],
                      "help": "Metric tracking the power consumed with some or all power rails gated off of the design specified on a per step\nand index basis. Power metric depend heavily on the method\nbeing used for extraction: dynamic vs static, workload\nspecification (vcd vs saif), power models, process/voltage/temperature.\nThe power sleeppower metric tries to capture the data that would\nusually be reflected inside a datasheet given the approprate\nfootnote conditions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: sleeppower",
                      "signature": null,
                      "switch": "-metric_sleeppower 'step index <float>'",
                      "type": "float",
                      "unit": "mw",
                      "value": null
                  },
                  "tasktime": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_tasktime 'dfm 0 10.0'",
                          "api: chip.set('metric','dfm','0','tasktime', 10.0)"
                      ],
                      "help": "Metric trakcing the total amount of time spent on a task from\nbeginning to end, including data transfers and pre/post\nprocessing.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: tasktime",
                      "signature": null,
                      "switch": "-metric_tasktime 'step index <float>'",
                      "type": "float",
                      "unit": "s",
                      "value": null
                  },
                  "totalarea": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_totalarea 'place 0 100.00'",
                          "api: chip.set('metric','place','0','totalarea', 100.00)"
                      ],
                      "help": "Metric tracking the total physical die area occupied by the design.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: totalarea",
                      "signature": null,
                      "switch": "-metric_totalarea 'step index <float>'",
                      "type": "float",
                      "unit": "um^2",
                      "value": null
                  },
                  "totaltime": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_totaltime 'dfm 0 10.0'",
                          "api: chip.set('metric','dfm','0','totaltime', 10.0)"
                      ],
                      "help": "Metric tracking the total amount of time spent from the beginning\nof the run up to and including the current step and index.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: totaltime",
                      "signature": null,
                      "switch": "-metric_totaltime 'step index <float>'",
                      "type": "float",
                      "unit": "s",
                      "value": null
                  },
                  "transistors": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_transistors 'place 0 100'",
                          "api: chip.set('metric','place','0','transistors', 50)"
                      ],
                      "help": "Metric tracking the total number of transistors in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: transistors",
                      "signature": null,
                      "switch": "-metric_transistors 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "unconstrained": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_unconstrained 'dfm 0 0'",
                          "api: chip.set('metric','dfm','0','unconstrained', 0)"
                      ],
                      "help": "Metric tracking the total number of unconstrained timing paths on a\nper step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: total unconstrained",
                      "signature": null,
                      "switch": "-metric_unconstrained 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "utilization": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_utilization 'place 0 50.00'",
                          "api: chip.set('metric','place','0','utilization', 50.00)"
                      ],
                      "help": "Metric tracking the area utilization of the design calculated as\n100 * (cellarea/totalarea).",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: area utilization",
                      "signature": null,
                      "switch": "-metric_utilization step index <float>",
                      "type": "float",
                      "unit": "%",
                      "value": null
                  },
                  "vias": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_vias 'place 0 100'",
                          "api: chip.set('metric','place','0','vias', 50)"
                      ],
                      "help": "Metric tracking the total number of vias in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: vias",
                      "signature": null,
                      "switch": "-metric_vias 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "warnings": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_warnings 'dfm 0 0'",
                          "api: chip.set('metric','dfm','0','warnings', 0)"
                      ],
                      "help": "Metric tracking the total number of warnings on a\nper step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: total warnings",
                      "signature": null,
                      "switch": "-metric_warnings 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "wirelength": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_wirelength 'place 0 100.0'",
                          "api: chip.set('metric','place','0','wirelength', 50.0)"
                      ],
                      "help": "Metric tracking the total wirelength of the design on a per step\nand index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: wirelength",
                      "signature": null,
                      "switch": "-metric_wirelength 'step index <float>'",
                      "type": "float",
                      "unit": "um",
                      "value": null
                  }
              }
          }
      },
      "model": {
          "formal": {
              "default": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -model_formal 'smv adder.smv'",
                      "api: chip.set('model','formal','smv','adder.smv')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Filepaths to formal models specified on a per filetype basis.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Model: Formal",
                  "signature": [],
                  "switch": "-model_formal 'filetype <file>'",
                  "type": "[file]",
                  "value": []
              }
          },
          "functional": {
              "default": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -model_functional 'systemc adder.sc'",
                      "api: chip.set('model','functional','systemc','adder.sc')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Filepaths to (fast) functional models specified on a per filetype\nbasis.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Model: Functional",
                  "signature": [],
                  "switch": "-model_functional 'filetype <file>'",
                  "type": "[file]",
                  "value": []
              }
          },
          "io": {
              "default": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -model_io 'ibis top.ibs'",
                      "api: chip.set('model','io','ibis','top.ibis')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Filepaths to IO models specified on a per filetype basis.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Model: IO",
                  "signature": [],
                  "switch": "-model_io 'filetype <file>'",
                  "type": "[file]",
                  "value": []
              }
          },
          "layout": {
              "default": {
                  "default": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -model_layout 'lef 10M adder.lef'",
                          "api: chip.set('model','layout','lef','10M','adder.lef')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepaths to abstract layout views specified on a per filetype\nand per stackup basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Model: Layout",
                      "signature": [],
                      "switch": "-model_layout 'filetype stackup <file>'",
                      "type": "[file]",
                      "value": []
                  }
              }
          },
          "power": {
              "default": {
                  "default": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -model_power 'apl ss adder.ss.apl'",
                          "api: chip.set('model','power','apl','ss','adder.ss.apl')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepaths to power/current models.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Model: Power",
                      "signature": [],
                      "switch": "-model_power 'filetype corner <file>'",
                      "type": "[file]",
                      "value": []
                  }
              }
          },
          "rtl": {
              "default": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -model_rtl 'systemc adder.sc'",
                      "api: chip.set('model','rtl','systemc','adder.sc')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Filepaths to cycle accurate model specified on a per filetype\nbasis.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Model: RTL",
                  "signature": [],
                  "switch": "-model_rtl 'filetype <file>'",
                  "type": "[file]",
                  "value": []
              }
          },
          "thermal": {
              "default": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -model_thermal 'delphi adder_thermal.sp'",
                      "api: chip.set('model','thermal','delphi','adder_thermal.sp')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Filepaths to thermal models.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Model: Thermal",
                  "signature": [],
                  "switch": "-model_thermal 'filetype corner <file>'",
                  "type": "[file]",
                  "value": []
              }
          },
          "timing": {
              "default": {
                  "default": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -model_timing 'nldm-libgz ss ss.lib.gz'",
                          "api: chip.set('model','timing','nldm-ldb','ss','ss.ldb')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepaths to static timing models specified on a per filetype and\nper corner basis.  Examples of filetypes include: nldm, nldm-ldb,\nnldm-libgz, ccs, ccs-libgz, ccs-ldb, scm, scm-libgz, scm-ldb,\naocv, aocv-libgz, aocv-ldb.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Model: Timing",
                      "signature": [],
                      "switch": "-model_timing 'filetype corner <file>'",
                      "type": "[file]",
                      "value": []
                  }
              }
          }
      },
      "option": {
          "autoinstall": {
              "defvalue": "false",
              "example": [
                  "cli: -autoinstall true'",
                  "api: chip.set('option', 'autoinstall', True)"
              ],
              "help": "Enables automatic installation of missing dependencies from\nthe registry.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Option: auto install packages",
              "signature": null,
              "switch": "-autoinstall <bool>",
              "type": "bool",
              "value": "false"
          },
          "bkpt": {
              "defvalue": [],
              "example": [
                  "cli: -bkpt place",
                  "api: chip.set('option,'bkpt','place')"
              ],
              "help": "List of step stop (break) points. If the step is a TCL\nbased tool, then the breakpoints stops the flow inside the\nEDA tool. If the step is a command line tool, then the flow\ndrops into a Python interpreter.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Breakpoint list",
              "signature": [],
              "switch": "-bkpt <str>",
              "type": "[str]",
              "value": []
          },
          "builddir": {
              "defvalue": "build",
              "example": [
                  "cli: -builddir ./build_the_future",
                  "api: chip.set('option', 'builddir','./build_the_future')"
              ],
              "help": "The default build directory is in the local './build' where SC was\nexecuted. The 'builddir' parameter can be used to set an alternate\ncompilation directory path.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Build directory",
              "signature": null,
              "switch": "-builddir <dir>",
              "type": "dir",
              "value": "build"
          },
          "cfg": {
              "author": [],
              "copy": "false",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -cfg mypdk.json",
                  "api: chip.set('option','cfg','mypdk.json')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "List of filepaths to JSON formatted schema configuration\nmanifests. The files are read in automatically when using the\n'sc' command line application. In Python programs, JSON manifests\ncan be merged into the current working manifest using the\nread_manifest() method.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Configuration manifest",
              "signature": [],
              "switch": "-cfg <file>",
              "type": "[file]",
              "value": []
          },
          "clean": {
              "defvalue": "false",
              "example": [
                  "cli: -clean",
                  "api: chip.set('option','clean',True)"
              ],
              "help": "Clean up all intermediate and non essential files at the end\nof a task, leaving the following:\n\n* log file\n* replay.sh\n* inputs/\n* outputs/\n* reports/\n* autogenerated manifests\n* any files generated by schema-specified regexes\n* files specified by :keypath:`tool, <tool>, keep`",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Clean up after run",
              "signature": null,
              "switch": "-clean <bool>",
              "type": "bool",
              "value": "false"
          },
          "cmdfile": {
              "author": [],
              "copy": "false",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -f design.f",
                  "api: chip.set('option', 'cmdfile','design.f')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "Read the specified file, and act as if all text inside it was specified\nas command line parameters. Supported by most verilog simulators\nincluding Icarus and Verilator. The format of the file is not strongly\nstandardized. Support for comments and environment variables within\nthe file varies and depends on the tool used. SC simply passes on\nthe filepath toe the tool executable.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Design compilation command file",
              "signature": [],
              "switch": "-f <file>",
              "type": "[file]",
              "value": []
          },
          "copyall": {
              "defvalue": "false",
              "example": [
                  "cli: -copyall",
                  "api: chip.set('option','copyall',True)"
              ],
              "help": "Specifies that all used files should be copied into the\nbuild directory, overriding the per schema entry copy\nsettings.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Copy all inputs to build directory",
              "signature": null,
              "switch": "-copyall <bool>",
              "type": "bool",
              "value": "false"
          },
          "credentials": {
              "author": [],
              "copy": "false",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -credentials /home/user/.sc/credentials",
                  "api: chip.set('option', 'credentials','/home/user/.sc/credentials')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "Filepath to credentials used for remote processing. If the\ncredentials parameter is empty, the remote processing client program\ntries to access the \".sc/credentials\" file in the user's home\ndirectory. The file supports the following fields:\n\nuserid=<user id>\nsecret_key=<secret key used for authentication>\nserver=<ipaddr or url>",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "User credentials file",
              "signature": [],
              "switch": "-credentials <file>'",
              "type": "[file]",
              "value": []
          },
          "define": {
              "defvalue": [],
              "example": [
                  "cli: -DCFG_ASIC=1",
                  "api: chip.set('option','define','CFG_ASIC=1')"
              ],
              "help": "Symbol definition for source preprocessor.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Design pre-processor symbol",
              "signature": [],
              "switch": "-D<str>",
              "type": "[str]",
              "value": []
          },
          "entrypoint": {
              "defvalue": null,
              "example": [
                  "cli: -entrypoint top",
                  "api: chip.set('option', 'entrypoint', 'top')"
              ],
              "help": "Alternative entrypoint for compilation and\nsimulation. The default entry point is 'design'.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Program entry point",
              "signature": null,
              "switch": "-entrypoint <str>",
              "type": "str",
              "value": null
          },
          "env": {
              "default": {
                  "defvalue": null,
                  "example": [
                      "cli: -env 'PDK_HOME /disk/mypdk'",
                      "api: chip.set('option','env', 'PDK_HOME', '/disk/mypdk')"
                  ],
                  "help": "Certain tools and reference flows require global environment\nvariables to be set. These variables can be managed externally or\nspecified through the env variable.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Environment variables",
                  "signature": null,
                  "switch": "-env 'key <str>",
                  "type": "str",
                  "value": null
              }
          },
          "flow": {
              "defvalue": null,
              "example": [
                  "cli: -flow asicfow",
                  "api: chip.set('option','flow','asicflow')"
              ],
              "help": "Sets the flow for the current run. The flow name\nmust match up with a 'flow' in the flowgraph",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Flow target",
              "signature": null,
              "switch": "-flow <str>",
              "type": "str",
              "value": null
          },
          "flowcontinue": {
              "defvalue": "false",
              "example": [
                  "cli: -flowcontinue",
                  "api: chip.set('option', 'flowcontinue', True)"
              ],
              "help": "Continue executing flow after a tool logs errors. The default\nbehavior is to quit executing the flow if a task ends and the errors\nmetric is greater than 0. Note that the flow will always cease\nexecuting if the tool returns a nonzero status code.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Continue flow on error",
              "signature": null,
              "switch": "-flowcontinue",
              "type": "bool",
              "value": "false"
          },
          "frontend": {
              "defvalue": "verilog",
              "example": [
                  "cli: -frontend systemverilog",
                  "api: chip.set('option','frontend', 'systemverilog')"
              ],
              "help": "Specifies the frontend that flows should use for importing and\nprocessing source files. Default option is 'verilog', also supports\n'systemverilog' and 'chisel'. When using the Python API, this parameter\nmust be configured before calling load_target().",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Compilation frontend",
              "signature": null,
              "switch": "-frontend <frontend>",
              "type": "str",
              "value": "verilog"
          },
          "hash": {
              "defvalue": "false",
              "example": [
                  "cli: -hash",
                  "api: chip.set('option','hash',True)"
              ],
              "help": "Enables hashing of all inputs and outputs during\ncompilation. The hash values are stored in the hashvalue\nfield of the individual parameters.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Enable file hashing",
              "signature": null,
              "switch": "-hash <bool>",
              "type": "bool",
              "value": "false"
          },
          "idir": {
              "defvalue": [],
              "example": [
                  "cli: +incdir+./mylib",
                  "api: chip.set('option','idir','./mylib')"
              ],
              "help": "Search paths to look for files included in the design using\nthe ```include`` statement.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Design search paths",
              "signature": [],
              "switch": [
                  "+incdir+<dir>",
                  "-I <dir>"
              ],
              "type": "[dir]",
              "value": []
          },
          "indexlist": {
              "defvalue": [],
              "example": [
                  "cli: -indexlist 0",
                  "api: chip.set('option','indexlist','0')"
              ],
              "help": "List of indices to execute. The default is to execute all\nindices for each step of a run.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Compilation index list",
              "signature": [],
              "switch": "-indexlist <index>",
              "type": "[str]",
              "value": []
          },
          "jobincr": {
              "defvalue": "false",
              "example": [
                  "cli: -jobincr",
                  "api: chip.set('option','jobincr',True)"
              ],
              "help": "Forces an auto-update of the jobname parameter if a directory\nmatching the jobname is found in the build directory. If the\njobname does not include a trailing digit, then a the number\n'1' is added to the jobname before updating the jobname\nparameter.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Autoincrement jobname",
              "signature": null,
              "switch": "-jobincr <bool>",
              "type": "bool",
              "value": "false"
          },
          "jobinput": {
              "default": {
                  "default": {
                      "defvalue": null,
                      "example": [
                          "cli: -jobinput 'cts 0 job0'",
                          "api:  chip.set('option','jobinput','cts,'0','job0')"
                      ],
                      "help": "Specifies jobname inputs for the current run() on a per step\nand per index basis. During execution, the default behavior is to\ncopy inputs from the current job.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Input job name",
                      "signature": null,
                      "switch": "-jobinput 'step index <str>'",
                      "type": "str",
                      "value": null
                  }
              }
          },
          "jobname": {
              "defvalue": "job0",
              "example": [
                  "cli: -jobname may1",
                  "api: chip.set('option','jobname','may1')"
              ],
              "help": "Jobname during invocation of run(). The jobname combined with a\ndefined director structure (<dir>/<design>/<jobname>/<step>/<index>)\nenables multiple levels of transparent job, step, and index\nintrospection.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Job name",
              "signature": null,
              "switch": "-jobname <str>",
              "type": "str",
              "value": "job0"
          },
          "jobscheduler": {
              "defvalue": null,
              "example": [
                  "cli: -jobscheduler slurm",
                  "api: chip.set('option','jobscheduler','slurm')"
              ],
              "help": "Sets the type of job scheduler to be used for each individual\nflowgraph steps. If the parameter is undefined, the steps are executed\non the same machine that the SC was launched on. If 'slurm' is used,\nthe host running the 'sc' command must be running a 'slurmctld' daemon\nmanaging a Slurm cluster. Additionally, the build directory ('-dir')\nmust be located in shared storage which can be accessed by all hosts\nin the cluster.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Job scheduler name",
              "signature": null,
              "switch": "-jobscheduler <str>",
              "type": "str",
              "value": null
          },
          "libext": {
              "defvalue": [],
              "example": [
                  "cli: +libext+sv",
                  "api: chip.set('option','libext','sv')"
              ],
              "help": "List of file extensions that should be used for finding modules.\nFor example, if -y is specified as ./lib\", and '.v' is specified as\nlibext then the files ./lib/\\*.v \", will be searched for\nmodule matches.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Design file extensions",
              "signature": [],
              "switch": "+libext+<str>",
              "type": "[str]",
              "value": []
          },
          "loglevel": {
              "defvalue": "INFO",
              "example": [
                  "cli: -loglevel INFO",
                  "api: chip.set('option', 'loglevel', 'INFO')"
              ],
              "help": "Provides explicit control over the level of debug logging printed.\nValid entries include INFO, DEBUG, WARNING, ERROR.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Logging level",
              "signature": null,
              "switch": "-loglevel <str>",
              "type": "str",
              "value": "INFO"
          },
          "metricoff": {
              "defvalue": [],
              "example": [
                  "cli: -metricoff 'wirelength'",
                  "api: chip.set('option','metricoff','wirelength')"
              ],
              "help": "List of metrics to supress when printing out the run\nsummary.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Metric summary filter",
              "signature": [],
              "switch": "-metricoff '<str>'",
              "type": "[str]",
              "value": []
          },
          "mode": {
              "defvalue": null,
              "example": [
                  "cli: -mode asic",
                  "api: chip.set('option','mode','asic')"
              ],
              "help": "Sets the operating mode of the compiler. Valid modes are:\nasic: RTL to GDS ASIC compilation\nfpga: RTL to bitstream FPGA compilation\nsim: simulation to verify design and compilation",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Compilation mode",
              "signature": null,
              "switch": "-mode <str>",
              "type": "str",
              "value": null
          },
          "msgcontact": {
              "defvalue": [],
              "example": [
                  "cli: -msgcontact 'wile.e.coyote@acme.com'",
                  "api: chip.set('option','msgcontact','wiley@acme.com')"
              ],
              "help": "A list of phone numbers or email addresses to message\non a event event within the msg_event param. Actual\nsupport for email and phone messages is platform\ndependent.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Message contact",
              "signature": [],
              "switch": "-msgcontact <str>",
              "type": "[str]",
              "value": []
          },
          "msgevent": {
              "defvalue": [],
              "example": [
                  "cli: -msgevent export",
                  "api: chip.set('option','msgevent','export')"
              ],
              "help": "A list of steps after which to notify a recipient. For\nexample if values of syn, place, cts are entered separate\nmessages would be sent after the completion of the syn,\nplace, and cts steps.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Message event trigger",
              "signature": [],
              "switch": "-msgevent <str>",
              "type": "[str]",
              "value": []
          },
          "nodisplay": {
              "defvalue": "false",
              "example": [
                  "cli: -nodisplay",
                  "api: chip.set('option','nodisplay',True)"
              ],
              "help": "The '-nodisplay' flag prevents SiliconCompiler from\nopening GUI windows such as the final metrics report.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Headless execution",
              "signature": null,
              "switch": "-nodisplay <bool>",
              "type": "bool",
              "value": "false"
          },
          "novercheck": {
              "defvalue": "false",
              "example": [
                  "cli: -novercheck",
                  "api: chip.set('option','novercheck',True)"
              ],
              "help": "Disables strict version checking on all invoked tools if True.\nThe list of supported version numbers is defined in the\n'version' parameter in the 'eda' dictionary for each tool.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Disable version checking",
              "signature": null,
              "switch": "-novercheck <bool>",
              "type": "bool",
              "value": "false"
          },
          "optmode": {
              "defvalue": "O0",
              "example": [
                  "cli: -O3",
                  "api: chip.set('option','optmode','O3')"
              ],
              "help": "The compiler has modes to prioritize run time and ppa. Modes\ninclude.\n\n(O0) = Exploration mode for debugging setup\n(O1) = Higher effort and better PPA than O0\n(O2) = Higher effort and better PPA than O1\n(O3) = Signoff quality. Better PPA and higher run times than O2\n(O4-O98) = Reserved (compiler/target dependent)\n(O99) = Experimental highest possible effort, may be unstable",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Optimization mode",
              "signature": null,
              "switch": "-O<str>",
              "type": "str",
              "value": "O0"
          },
          "param": {
              "default": {
                  "defvalue": null,
                  "example": [
                      "cli: -param 'N 64'",
                      "api: chip.set('option','param','N','64')"
                  ],
                  "help": "Sets a top verilog level design module parameter. The value\nis limited to basic data literals. The parameter override is\npassed into tools such as Verilator and Yosys. The parameters\nsupport Verilog integer literals (64'h4, 2'b0, 4) and strings.\nName of the top level module to compile.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Design parameter",
                  "signature": null,
                  "switch": "-param 'name <str>'",
                  "type": "str",
                  "value": null
              }
          },
          "pdk": {
              "defvalue": null,
              "example": [
                  "cli: -pdk asap7",
                  "api: chip.set('option','pdk','asap7')"
              ],
              "help": "Sets the pdk for the current run. The pdk name\nmust match up with a 'pdk' loaded with load_pdk.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "PDK target",
              "signature": null,
              "switch": "-pdk <str>",
              "type": "str",
              "value": null
          },
          "quiet": {
              "defvalue": "false",
              "example": [
                  "cli: -quiet",
                  "api: chip.set('option','quiet',True)"
              ],
              "help": "The -quiet option forces all steps to print to a log file.\nThis can be useful with Modern EDA tools which print\nsignificant content to the screen.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Quiet execution",
              "signature": null,
              "switch": "-quiet <bool>",
              "type": "bool",
              "value": "false"
          },
          "registry": {
              "defvalue": [],
              "example": [
                  "cli: -registry '~/myregistry'",
                  "api: chip.set('option','registry','~/myregistry')"
              ],
              "help": "List of Silicon Unified Packager (SUP) registry directories.\nDirectories can be local file system folders or\npublicly available registries served up over http. The naming\nconvention for registry packages is:\n<name>/<name>-<version>.json(.<gz>)?",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Option: package registry",
              "signature": [],
              "switch": "-registry <dir>",
              "type": "[dir]",
              "value": []
          },
          "relax": {
              "defvalue": "false",
              "example": [
                  "cli: -relax",
                  "api: chip.set('option','relax',True)"
              ],
              "help": "Specifies that tools should be lenient and suppress some\nwarnings that may or may not indicate design issues.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Relax RTL linting",
              "signature": null,
              "switch": "-relax <bool>",
              "type": "bool",
              "value": "false"
          },
          "remote": {
              "defvalue": "false",
              "example": [
                  "cli: -remote",
                  "api: chip.set('option','remote', True)"
              ],
              "help": "Sends job for remote processing if set to true. The remote\noption requires a credentials file to be placed in the home\ndirectory. Fore more information, see the credentials\nparameter.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Enable remote processing",
              "signature": null,
              "switch": "-remote <bool>",
              "type": "bool",
              "value": "false"
          },
          "resume": {
              "defvalue": "false",
              "example": [
                  "cli: -resume",
                  "api: chip.set('option','resume',True)"
              ],
              "help": "If results exist for current job, then don't re-run any steps that\nhad at least one index run successfully. Useful for debugging a\nflow that failed partway through.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Resume build",
              "signature": null,
              "switch": "-resume <bool>",
              "type": "bool",
              "value": "false"
          },
          "scpath": {
              "defvalue": [],
              "example": [
                  "cli: -scpath '/home/$USER/sclib'",
                  "api: chip.set('option', 'scpath','/home/$USER/sclib')"
              ],
              "help": "Specifies python modules paths for target import.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Search path",
              "signature": [],
              "switch": "-scpath <dir>",
              "type": "[dir]",
              "value": []
          },
          "show": {
              "defvalue": "false",
              "example": [
                  "cli: -show",
                  "api: chip.set('option','show',True)"
              ],
              "help": "Specifies that the final hardware layout should be\nshown after the compilation has been completed. The\nfinal layout and tool used to display the layout is\nflow dependent.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Show layout",
              "signature": null,
              "switch": "-show <bool>",
              "type": "bool",
              "value": "false"
          },
          "showtool": {
              "default": {
                  "defvalue": null,
                  "example": [
                      "cli: -showtool 'gds klayout'",
                      "api: chip.set('option','showtool','gds','klayout')"
                  ],
                  "help": "Selects the tool to use by the show function for displaying\nthe specified filetype.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Select data display tool",
                  "signature": null,
                  "switch": "-showtool 'filetype <tool>'",
                  "type": "str",
                  "value": null
              }
          },
          "skipall": {
              "defvalue": "false",
              "example": [
                  "cli: -skipall",
                  "api: chip.set('option','skipall',True)"
              ],
              "help": "Skips the execution of all tools in run(), enabling a quick\ncheck of tool and setup without having to run through each\nstep of a flow to completion.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Skip all tasks",
              "signature": null,
              "switch": "-skipall <bool>",
              "type": "bool",
              "value": "false"
          },
          "skipcheck": {
              "defvalue": "false",
              "example": [
                  "cli: -skipcheck",
                  "api: chip.set('option','skipcheck',True)"
              ],
              "help": "Bypasses the strict runtime manifest check. Can be used for\naccelerating initial bringup of tool/flow/pdk/libs targets.\nThe flag should not be used for production compilation.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Skip manifest check",
              "signature": null,
              "switch": "-skipcheck <bool>",
              "type": "bool",
              "value": "false"
          },
          "skipstep": {
              "defvalue": [],
              "example": [
                  "cli: -skipstep lvs",
                  "api: chip.set('option','skipstep','lvs')"
              ],
              "help": "List of steps to skip during execution.The default is to\nexecute all steps  defined in the flow graph.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Skip step list",
              "signature": [],
              "switch": "-skipstep <str>",
              "type": "[str]",
              "value": []
          },
          "steplist": {
              "defvalue": [],
              "example": [
                  "cli: -steplist 'import'",
                  "api: chip.set('option','steplist','import')"
              ],
              "help": "List of steps to execute. The default is to execute all steps\ndefined in the flow graph.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Compilation step list",
              "signature": [],
              "switch": "-steplist <step>",
              "type": "[str]",
              "value": []
          },
          "target": {
              "defvalue": null,
              "example": [
                  "cli: -target freepdk45_demo",
                  "api: chip.set('option','target','freepdk45_demo')"
              ],
              "help": "Sets a target module to be used for compilation. The target\nmodule must set up all paramaters needed. The target module\nmay load multiple flows and libraries.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Compilation target",
              "signature": null,
              "switch": "-target <str>",
              "type": "str",
              "value": null
          },
          "trace": {
              "defvalue": "false",
              "example": [
                  "cli: -trace",
                  "api: chip.set('option','trace',True)"
              ],
              "help": "Enables debug tracing during compilation and/or runtime.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Enable debug traces",
              "signature": null,
              "switch": "-trace <bool>",
              "type": "bool",
              "value": "false"
          },
          "track": {
              "defvalue": "false",
              "example": [
                  "cli: -track",
                  "api: chip.set('option','track',True)"
              ],
              "help": "Turns on tracking of all 'record' parameters during each\ntask. Tracking will result in potentially sensitive data\nbeing recorded in the manifest so only turn on this feature\nif you have control of the final manifest.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Enable provenance tracking",
              "signature": null,
              "switch": "-track <bool>",
              "type": "bool",
              "value": "false"
          },
          "vlib": {
              "author": [],
              "copy": "false",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -v './mylib.v'",
                  "api: chip.set('option', 'vlib','./mylib.v')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "List of library files to be read in. Modules found in the\nlibraries are not interpreted as root modules.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Design libraries",
              "signature": [],
              "switch": "-v <file>",
              "type": "[file]",
              "value": []
          },
          "ydir": {
              "defvalue": [],
              "example": [
                  "cli: -y './mylib'",
                  "api: chip.set('option','ydir','./mylib')"
              ],
              "help": "Search paths to look for verilog modules found in the the\nsource list. The import engine will look for modules inside\nfiles with the specified +libext+ param suffix.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Design module search paths",
              "signature": [],
              "switch": "-y <dir>",
              "type": "[dir]",
              "value": []
          }
      },
      "output": {
          "default": {
              "author": [],
              "copy": "false",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -output 'verilog hello_world.v'",
                  "api: chip.set(output,'verilog','hello_world.v')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "List of output files specifed by type. The filetype name must\nalign with the parameter names within the flow and tool setup\nscripts. Examples of acceptable file types include python, c,\nsystemc, verilog, vhdl, netlist, def, gds, gerber, saif, sdc,\nsaif, vcd, spef, sdf.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Output files",
              "signature": [],
              "switch": "-output 'filetype <file>'",
              "type": "[file]",
              "value": []
          }
      },
      "package": {
          "author": {
              "default": {
                  "email": {
                      "defvalue": null,
                      "example": [
                          "cli: -package_author_email 'wiley wiley@acme.com'",
                          "api: chip.set('package','author','wiley','email','wiley@acme.com')"
                      ],
                      "help": "Package author email provided with full name as key and\nemail as value.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Package: author email",
                      "signature": null,
                      "switch": "-package_author_email 'userid <str>'",
                      "type": "str",
                      "value": null
                  },
                  "location": {
                      "defvalue": null,
                      "example": [
                          "cli: -package_author_location 'wiley wiley@acme.com'",
                          "api: chip.set('package','author','wiley','location','wiley@acme.com')"
                      ],
                      "help": "Package author location provided with full name as key and\nlocation as value.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Package: author location",
                      "signature": null,
                      "switch": "-package_author_location 'userid <str>'",
                      "type": "str",
                      "value": null
                  },
                  "name": {
                      "defvalue": null,
                      "example": [
                          "cli: -package_author_name 'wiley wiley@acme.com'",
                          "api: chip.set('package','author','wiley','name','wiley@acme.com')"
                      ],
                      "help": "Package author name provided with full name as key and\nname as value.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Package: author name",
                      "signature": null,
                      "switch": "-package_author_name 'userid <str>'",
                      "type": "str",
                      "value": null
                  },
                  "organization": {
                      "defvalue": null,
                      "example": [
                          "cli: -package_author_organization 'wiley wiley@acme.com'",
                          "api: chip.set('package','author','wiley','organization','wiley@acme.com')"
                      ],
                      "help": "Package author organization provided with full name as key and\norganization as value.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Package: author organization",
                      "signature": null,
                      "switch": "-package_author_organization 'userid <str>'",
                      "type": "str",
                      "value": null
                  },
                  "publickey": {
                      "defvalue": null,
                      "example": [
                          "cli: -package_author_publickey 'wiley wiley@acme.com'",
                          "api: chip.set('package','author','wiley','publickey','wiley@acme.com')"
                      ],
                      "help": "Package author publickey provided with full name as key and\npublickey as value.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Package: author publickey",
                      "signature": null,
                      "switch": "-package_author_publickey 'userid <str>'",
                      "type": "str",
                      "value": null
                  },
                  "username": {
                      "defvalue": null,
                      "example": [
                          "cli: -package_author_username 'wiley wiley@acme.com'",
                          "api: chip.set('package','author','wiley','username','wiley@acme.com')"
                      ],
                      "help": "Package author username provided with full name as key and\nusername as value.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Package: author username",
                      "signature": null,
                      "switch": "-package_author_username 'userid <str>'",
                      "type": "str",
                      "value": null
                  }
              }
          },
          "dependency": {
              "default": {
                  "defvalue": [],
                  "example": [
                      "cli: -package_dependency 'hello 1.0'",
                      "api: chip.set('package','dependency','hello','1.0')"
                  ],
                  "help": "Package dependencies specified as a key value pair.\nVersions shall follow the semver standard.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: version dependancies",
                  "signature": [],
                  "switch": "-package_dependency 'module <str>'",
                  "type": "[str]",
                  "value": []
              }
          },
          "depgraph": {
              "default": {
                  "defvalue": [],
                  "example": [
                      "cli: -package_depgraph 'top (cpu,1.0.1)'",
                      "api: chip.set('package','depgraph','top',('cpu','1.0.1'))"
                  ],
                  "help": "List of Silicon Unified Packager (SUP) dependencies\nused by the design specified on a per module basis a\nlist of string tuples ('name','version').",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package dependency list",
                  "signature": [],
                  "switch": "-package_depgraph 'module <(str,str)>'",
                  "type": "[(str,str)]",
                  "value": []
              }
          },
          "description": {
              "defvalue": null,
              "example": [
                  "cli: -package_description 'Yet another cpu'",
                  "api: chip.set('package','description','Yet another cpu')"
              ],
              "help": "Package short one line description for package\nmanagers and summary reports.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: description",
              "signature": null,
              "switch": "-package_description <str>",
              "type": "str",
              "value": null
          },
          "doc": {
              "datasheet": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_datasheet datasheet.pdf",
                      "api: chip.set('package','doc',datasheet,'datasheet.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of datasheet documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: datasheet document",
                  "signature": [],
                  "switch": "-package_doc_datasheet <str",
                  "type": "[file]",
                  "value": []
              },
              "homepage": {
                  "defvalue": null,
                  "example": [
                      "cli: -package_doc_homepage index.html",
                      "api: chip.set('package','doc', 'homepage','index.html')"
                  ],
                  "help": "Package documentation homepage. Filepath to design docs homepage.\nComplex designs can can include a long non standard list of\ndocuments dependent.  A single html entry point can be used to\npresent an organized documentation dashboard to the designer.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: documentation homepage",
                  "signature": null,
                  "switch": "-package_doc_homepage <str>",
                  "type": "str",
                  "value": null
              },
              "quickstart": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_quickstart quickstart.pdf",
                      "api: chip.set('package','doc',quickstart,'quickstart.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of quickstart documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: quickstart document",
                  "signature": [],
                  "switch": "-package_doc_quickstart <str",
                  "type": "[file]",
                  "value": []
              },
              "reference": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_reference reference.pdf",
                      "api: chip.set('package','doc',reference,'reference.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of reference documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: reference document",
                  "signature": [],
                  "switch": "-package_doc_reference <str",
                  "type": "[file]",
                  "value": []
              },
              "releasenotes": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_releasenotes releasenotes.pdf",
                      "api: chip.set('package','doc',releasenotes,'releasenotes.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of releasenotes documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: releasenotes document",
                  "signature": [],
                  "switch": "-package_doc_releasenotes <str",
                  "type": "[file]",
                  "value": []
              },
              "signoff": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_signoff signoff.pdf",
                      "api: chip.set('package','doc',signoff,'signoff.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of signoff documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: signoff document",
                  "signature": [],
                  "switch": "-package_doc_signoff <str",
                  "type": "[file]",
                  "value": []
              },
              "testplan": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_testplan testplan.pdf",
                      "api: chip.set('package','doc',testplan,'testplan.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of testplan documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: testplan document",
                  "signature": [],
                  "switch": "-package_doc_testplan <str",
                  "type": "[file]",
                  "value": []
              },
              "tutorial": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_tutorial tutorial.pdf",
                      "api: chip.set('package','doc',tutorial,'tutorial.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of tutorial documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: tutorial document",
                  "signature": [],
                  "switch": "-package_doc_tutorial <str",
                  "type": "[file]",
                  "value": []
              },
              "userguide": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_userguide userguide.pdf",
                      "api: chip.set('package','doc',userguide,'userguide.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of userguide documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: userguide document",
                  "signature": [],
                  "switch": "-package_doc_userguide <str",
                  "type": "[file]",
                  "value": []
              }
          },
          "homepage": {
              "defvalue": null,
              "example": [
                  "cli: -package_homepage index.html",
                  "api: chip.set('package','homepage','index.html')"
              ],
              "help": "Package homepage.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: project homepage",
              "signature": null,
              "switch": "-package_homepage <str>",
              "type": "str",
              "value": null
          },
          "keyword": {
              "defvalue": null,
              "example": [
                  "cli: -package_keyword cpu",
                  "api: chip.set('package','keyword','cpu')"
              ],
              "help": "Package keyword(s) used to characterize package.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: keyword",
              "signature": null,
              "switch": "-package_keyword <str>",
              "type": "str",
              "value": null
          },
          "license": {
              "defvalue": [],
              "example": [
                  "cli: -package_license 'Apache-2.0'",
                  "api: chip.set('package','license','Apache-2.0')"
              ],
              "help": "Package list of SPDX license identifiers.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: license identifiers",
              "signature": [],
              "switch": "-package_license <str>",
              "type": "[str]",
              "value": []
          },
          "licensefile": {
              "author": [],
              "copy": "false",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -package_licensefile './LICENSE'",
                  "api: chip.set('package','licensefile','./LICENSE')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "Package list of license files for to be\napplied in cases when a SPDX identifier is not available.\n(eg. proprietary licenses).list of SPDX license identifiers.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: license files",
              "signature": [],
              "switch": "-package_licensefile <file>",
              "type": "[file]",
              "value": []
          },
          "location": {
              "defvalue": [],
              "example": [
                  "cli: -package_location 'mars'",
                  "api: chip.set('package','location','mars')"
              ],
              "help": "Package country of origin specified as standardized\ninternational country codes. The field can be left blank\nif the location is unknown or global.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: location",
              "signature": [],
              "switch": "-package_location <file>",
              "type": "[str]",
              "value": []
          },
          "name": {
              "defvalue": null,
              "example": [
                  "cli: -package_name yac",
                  "api: chip.set('package','name','yac')"
              ],
              "help": "Package name.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: name",
              "signature": null,
              "switch": "-package_name <str>",
              "type": "str",
              "value": null
          },
          "organization": {
              "defvalue": [],
              "example": [
                  "cli: -package_organization 'humanity'",
                  "api: chip.set('package','organization','humanity')"
              ],
              "help": "Package sponsoring organization. The field can be left\nblank if not applicable.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: sponsoring organization",
              "signature": [],
              "switch": "-package_organization <str>",
              "type": "[str]",
              "value": []
          },
          "publickey": {
              "defvalue": null,
              "example": [
                  "cli: -package_publickey '6EB695706EB69570'",
                  "api: chip.set('package','publickey','6EB695706EB69570')"
              ],
              "help": "Package public project key.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: public key",
              "signature": null,
              "switch": "-package_publickey <str>",
              "type": "str",
              "value": null
          },
          "repo": {
              "defvalue": [],
              "example": [
                  "cli: -package_repo 'git@github.com:aolofsson/oh.git'",
                  "api: chip.set('package','repo','git@github.com:aolofsson/oh.git')"
              ],
              "help": "Package IP address to source code repository.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: code repository",
              "signature": [],
              "switch": "-package_repo <str>",
              "type": "[str]",
              "value": []
          },
          "target": {
              "defvalue": [],
              "example": [
                  "cli: -package_target 'asicflow_freepdk45'",
                  "api: chip.set('package','target','asicflow_freepdk45')"
              ],
              "help": "Package list of qualified compilation targets.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: qualified targets",
              "signature": [],
              "switch": "-package_target <str>",
              "type": "[str]",
              "value": []
          },
          "version": {
              "defvalue": null,
              "example": [
                  "cli: -package_version 1.0",
                  "api: chip.set('package','version','1.0')"
              ],
              "help": "Package version. Can be a branch, tag, commit hash,\nor a semver compatible version.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: version",
              "signature": null,
              "switch": "-package_version <str>",
              "type": "str",
              "value": null
          }
      },
      "pdk": {
          "default": {
              "aprtech": {
                  "default": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_aprtech 'asap7 openroad M10 12t lef tech.lef'",
                                      "api: chip.set('pdk','asap7','aprtech','openroad','M10','12t','lef','tech.lef')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Technology file containing setup information needed to enable DRC clean APR\nfor the specified stackup, libarch, and format. The 'libarch' specifies the\nlibrary architecture (e.g. library height). For example a PDK with support\nfor 9 and 12 track libraries might have 'libarchs' called 9t and 12t.\nThe standard filetype for specifying place and route design rules for a\nprocess node is through a 'lef' format technology file. The\n'filetype' used in the aprtech is used by the tool specific APR TCL scripts\nto set up the technology parameters. Some tools may require additional\nfiles beyond the tech.lef file. Examples of extra file types include\nantenna, tracks, tapcell, viarules, em.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: APR technology files",
                                  "signature": [],
                                  "switch": "-pdk_aprtech 'pdkname tool stackup libarch filetype <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  }
              },
              "d0": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_d0 'asap7 0.1'",
                      "api:  chip.set('pdk', 'asap7', 'd0', 0.1)"
                  ],
                  "help": "Process defect density (d0) expressed as random defects per cm^2. The\nvalue is used to calculate yield losses as a function of area, which in\nturn affects the chip full factory costs. Two yield models are\nsupported: Poisson (default), and Murphy. The Poisson based yield is\ncalculated as dy = exp(-area * d0/100). The Murphy based yield is\ncalculated as dy = ((1-exp(-area * d0/100))/(area * d0/100))^2.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: process defect density",
                  "signature": null,
                  "switch": "-pdk_d0 'pdkname <float>'",
                  "type": "float",
                  "value": null
              },
              "density": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_density 'asap7 100e6'",
                      "api:  chip.set('pdk', 'asap7', 'density', 10e6)"
                  ],
                  "help": "Approximate logic density expressed as # transistors / mm^2\ncalculated as:\n0.6 * (Nand2 Transistor Count) / (Nand2 Cell Area) +\n0.4 * (Register Transistor Count) / (Register Cell Area)\nThe value is specified for a fixed standard cell library within a node\nand will differ depending on the library vendor, library track height\nand library type. The value can be used to to normalize the effective\ndensity reported for the design across different process nodes. The\nvalue can be derived from a variety of sources, including the PDK DRM,\nlibrary LEFs, conference presentations, and public analysis.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: transistor density",
                  "signature": null,
                  "switch": "-pdk_density 'pdkname <float>'",
                  "type": "float",
                  "value": null
              },
              "devmodel": {
                  "default": {
                      "default": {
                          "default": {
                              "author": [],
                              "copy": "false",
                              "date": [],
                              "defvalue": [],
                              "example": [
                                  "cli: -pdk_devmodel 'asap7 xyce spice M10 asap7.sp'",
                                  "api: chip.set('pdk','asap7','devmodel','xyce','spice','M10','asap7.sp')"
                              ],
                              "filehash": [],
                              "hashalgo": "sha256",
                              "help": "List of filepaths to PDK device models for different simulation\npurposes and for different tools. Examples of device model types\ninclude spice, aging, electromigration, radiation. An example of a\n'spice' tool is xyce. Device models are specified on a per metal stack\nbasis. Process nodes with a single device model across all stacks will\nhave a unique parameter record per metal stack pointing to the same\ndevice model file.  Device types and tools are dynamic entries\nthat depend on the tool setup and device technology. Pseud-standardized\ndevice types include spice, em (electromigration), and aging.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: device models",
                              "signature": [],
                              "switch": "-pdk_devmodel 'pdkname tool simtype stackup <file>'",
                              "type": "[file]",
                              "value": []
                          }
                      }
                  }
              },
              "directory": {
                  "default": {
                      "default": {
                          "default": {
                              "defvalue": [],
                              "example": [
                                  "cli: -pdk_directory 'asap7 xyce rfmodel M10 rftechdir'",
                                  "api: chip.set('pdk','asap7','directory','xyce','rfmodel','M10','rftechdir')"
                              ],
                              "help": "List of named directories specified on a per tool and per stackup basis.\nThe parameter should only be used for specifying files that are\nnot directly  supported by the SiliconCompiler PDK schema.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: special directory",
                              "signature": [],
                              "switch": "-pdk_directory 'pdkname tool key stackup <file>'",
                              "type": "[dir]",
                              "value": []
                          }
                      }
                  }
              },
              "display": {
                  "default": {
                      "default": {
                          "author": [],
                          "copy": "false",
                          "date": [],
                          "defvalue": [],
                          "example": [
                              "cli: -pdk_display 'asap7 klayout M10 display.lyt'",
                              "api: chip.set('pdk','asap7','display','klayout','M10','display.cfg')"
                          ],
                          "filehash": [],
                          "hashalgo": "sha256",
                          "help": "Display configuration files describing colors and pattern schemes for\nall layers in the PDK. The display configuration file is entered on a\nstackup and tool basis.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "global",
                          "shorthelp": "PDK: display file",
                          "signature": [],
                          "switch": "-pdk_display 'pdkname tool stackup <file>'",
                          "type": "[file]",
                          "value": []
                      }
                  }
              },
              "doc": {
                  "datasheet": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_datasheet 'asap7 datasheet.pdf'",
                          "api: chip.set('pdk','asap7','doc',datasheet,'datasheet.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to datasheet document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: datasheet",
                      "signature": [],
                      "switch": "-pdk_doc_datasheet 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "homepage": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_homepage 'asap7 index.html'",
                          "api: chip.set('pdk','asap7','doc','homepage','index.html')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to PDK docs homepage. Modern PDKs can include tens or\nhundreds of individual documents. A single html entry point can\nbe used to present an organized documentation dashboard to the\ndesigner.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: documentation homepage",
                      "signature": [],
                      "switch": "-pdk_doc_homepage 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "install": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_install 'asap7 install.pdf'",
                          "api: chip.set('pdk','asap7','doc',install,'install.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to install document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: install",
                      "signature": [],
                      "switch": "-pdk_doc_install 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "quickstart": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_quickstart 'asap7 quickstart.pdf'",
                          "api: chip.set('pdk','asap7','doc',quickstart,'quickstart.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to quickstart document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: quickstart",
                      "signature": [],
                      "switch": "-pdk_doc_quickstart 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "reference": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_reference 'asap7 reference.pdf'",
                          "api: chip.set('pdk','asap7','doc',reference,'reference.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to reference document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: reference",
                      "signature": [],
                      "switch": "-pdk_doc_reference 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "releasenotes": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_releasenotes 'asap7 releasenotes.pdf'",
                          "api: chip.set('pdk','asap7','doc',releasenotes,'releasenotes.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to releasenotes document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: releasenotes",
                      "signature": [],
                      "switch": "-pdk_doc_releasenotes 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "tutorial": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_tutorial 'asap7 tutorial.pdf'",
                          "api: chip.set('pdk','asap7','doc',tutorial,'tutorial.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to tutorial document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: tutorial",
                      "signature": [],
                      "switch": "-pdk_doc_tutorial 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "userguide": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_userguide 'asap7 userguide.pdf'",
                          "api: chip.set('pdk','asap7','doc',userguide,'userguide.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to userguide document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: userguide",
                      "signature": [],
                      "switch": "-pdk_doc_userguide 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  }
              },
              "drc": {
                  "runset": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_drc_runset 'asap7 magic M10 basic $PDK/drc.rs'",
                                      "api: chip.set('pdk', 'asap7','drc','runset','magic','M10','basic','$PDK/drc.rs')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Runset files for DRC task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: DRC runset files",
                                  "signature": [],
                                  "switch": "-pdk_drc_runset 'pdkname tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  },
                  "waiver": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_drc_waiver 'asap7 magic M10 basic $PDK/drc.txt'",
                                      "api: chip.set('pdk', 'asap7','drc','waiver','magic','M10','basic','$PDK/drc.txt')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Waiver files for DRC task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: DRC waiver files",
                                  "signature": [],
                                  "switch": "-pdk_drc_waiver 'tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  }
              },
              "edgemargin": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_edgemargin 'asap7 1'",
                      "api:  chip.set('pdk', 'asap7', 'edgemargin', 1)"
                  ],
                  "help": "Keep-out distance/margin from the edge inwards. The edge\nis prone to chipping and need special treatment that preclude\nplacement of designs in this area. The edge value is used to\ncalculate effective units per wafer/panel and full factory cost.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: wafer edge keep-out margin",
                  "signature": null,
                  "switch": "-pdk_edgemargin 'pdkname <float>'",
                  "type": "float",
                  "unit": "mm",
                  "value": null
              },
              "erc": {
                  "runset": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_erc_runset 'asap7 magic M10 basic $PDK/erc.rs'",
                                      "api: chip.set('pdk', 'asap7','erc','runset','magic','M10','basic','$PDK/erc.rs')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Runset files for ERC task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: ERC runset files",
                                  "signature": [],
                                  "switch": "-pdk_erc_runset 'pdkname tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  },
                  "waiver": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_erc_waiver 'asap7 magic M10 basic $PDK/erc.txt'",
                                      "api: chip.set('pdk', 'asap7','erc','waiver','magic','M10','basic','$PDK/erc.txt')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Waiver files for ERC task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: ERC waiver files",
                                  "signature": [],
                                  "switch": "-pdk_erc_waiver 'tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  }
              },
              "file": {
                  "default": {
                      "default": {
                          "default": {
                              "author": [],
                              "copy": "false",
                              "date": [],
                              "defvalue": [],
                              "example": [
                                  "cli: -pdk_file 'asap7 xyce spice M10 asap7.sp'",
                                  "api: chip.set('pdk','asap7','file','xyce','spice','M10','asap7.sp')"
                              ],
                              "filehash": [],
                              "hashalgo": "sha256",
                              "help": "List of named files specified on a per tool and per stackup basis.\nThe parameter should only be used for specifying files that are\nnot directly  supported by the SiliconCompiler PDK schema.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: special file",
                              "signature": [],
                              "switch": "-pdk_file 'pdkname tool key stackup <file>'",
                              "type": "[file]",
                              "value": []
                          }
                      }
                  }
              },
              "fill": {
                  "runset": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_fill_runset 'asap7 magic M10 basic $PDK/fill.rs'",
                                      "api: chip.set('pdk', 'asap7','fill','runset','magic','M10','basic','$PDK/fill.rs')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Runset files for FILL task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: FILL runset files",
                                  "signature": [],
                                  "switch": "-pdk_fill_runset 'pdkname tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  },
                  "waiver": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_fill_waiver 'asap7 magic M10 basic $PDK/fill.txt'",
                                      "api: chip.set('pdk', 'asap7','fill','waiver','magic','M10','basic','$PDK/fill.txt')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Waiver files for FILL task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: FILL waiver files",
                                  "signature": [],
                                  "switch": "-pdk_fill_waiver 'tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  }
              },
              "foundry": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_foundry 'asap7 virtual'",
                      "api:  chip.set('pdk', 'asap7', 'foundry', 'virtual')"
                  ],
                  "help": "Name of foundry corporation. Examples include intel, gf, tsmc,\nsamsung, skywater, virtual. The 'virtual' keyword is reserved for\nsimulated non-manufacturable processes.",
                  "lock": "false",
                  "notes": null,
                  "require": "asic",
                  "scope": "global",
                  "shorthelp": "PDK: foundry name",
                  "signature": null,
                  "switch": "-pdk_foundry 'pdkname <str>'",
                  "type": "str",
                  "value": null
              },
              "grid": {
                  "default": {
                      "default": {
                          "adj": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_adj 'asap7 M10 m2 0.5'",
                                  "api: chip.set('pdk','asap7','grid','M10','m2','adj','0.5')"
                              ],
                              "help": "Defines the routing resources adjustments for the design on a per layer\nbasis. The value is expressed as a fraction from 0 to 1. A value of\n0.5 reduces the routing resources by 50%. If not defined, 100%\nrouting resource utilization is permitted.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid resource adjustment",
                              "signature": null,
                              "switch": "-pdk_grid_adj 'pdkname stackup layer <float>'",
                              "type": "float",
                              "value": null
                          },
                          "cap": {
                              "default": {
                                  "defvalue": null,
                                  "example": [
                                      "cli: -pdk_grid_cap 'asap7 M10 m2 fast 0.2'",
                                      "api: chip.set('pdk','asap7','grid','M10','m2','cap','fast','0.2')"
                                  ],
                                  "help": "Unit capacitance of a wire defined by the grid width and spacing values\nin the 'grid' structure specified on a per\nstackup, metal, and corner basis. As a rough rule of thumb, this value\ntends to stay around 0.2ff/um. This number should only be used for\nreality confirmation. Accurate analysis should use the PEX models.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: routing grid unit capacitance",
                                  "signature": null,
                                  "switch": "-pdk_grid_cap 'pdkname stackup layer corner <float>''",
                                  "type": "float",
                                  "unit": "ff/um",
                                  "value": null
                              }
                          },
                          "dir": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_dir 'asap7 M10 m1 horizontal'",
                                  "api: chip.set('pdk','asap7','grid','M10','m1','dir','horizontal')"
                              ],
                              "help": "Preferred routing direction specified on a per stackup\nand per metal basis. Valid routing directions are horizontal\nand vertical.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid preferred direction",
                              "signature": null,
                              "switch": "-pdk_grid_dir 'pdkname stackup layer <str>'",
                              "type": "str",
                              "value": null
                          },
                          "name": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_name 'asap7 M10 metal1 m1'",
                                  "api: chip.set('pdk','asap7','grid','M10','metal1','name','m1')"
                              ],
                              "help": "Maps PDK metal names to the SC standardized layer stack\nstarting with m1 as the lowest routing layer and ending\nwith m<n> as the highest routing layer. The map is\nspecified on a per metal stack basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid name map",
                              "signature": null,
                              "switch": "-pdk_grid_name 'pdkname stackup layer <str>'",
                              "type": "str",
                              "value": null
                          },
                          "res": {
                              "default": {
                                  "defvalue": null,
                                  "example": [
                                      "cli: -pdk_grid_res 'asap7 M10 m2 fast 0.2'",
                                      "api: chip.set('pdk','asap7','grid','M10','m2','res','fast','0.2')"
                                  ],
                                  "help": "Resistance of a wire defined by the grid width and spacing values\nin the 'grid' structure specified as ohms/um on a per\nstackup, metal, and corner basis. The parameter is only meant to be\nused as a sanity check and for coarse design planning. Accurate\nanalysis should use the TCAD PEX models.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: routing grid unit resistance",
                                  "signature": null,
                                  "switch": "-pdk_grid_res 'pdkname stackup layer corner <float>''",
                                  "type": "float",
                                  "unit": "ohm/um",
                                  "value": null
                              }
                          },
                          "tcr": {
                              "default": {
                                  "defvalue": null,
                                  "example": [
                                      "cli: -pdk_grid_tcr 'asap7 M10 m2 fast 0.2'",
                                      "api: chip.set('pdk','asap7','grid','M10','m2','tcr','fast','0.2')"
                                  ],
                                  "help": "Temperature coefficient of resistance of the wire defined by the grid\nwidth and spacing values in the 'grid' structure on a per stackup,\nlayer, and corner basis. The number is only meant to be used as a\nsanity check and for coarse design planning. Accurate analysis\nshould use the PEX models.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: routing grid temperature coefficient",
                                  "signature": null,
                                  "switch": "-pdk_grid_tcr 'pdkname stackup layer corner <float>'",
                                  "type": "float",
                                  "unit": "%/degree(T)",
                                  "value": null
                              }
                          },
                          "xoffset": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_xoffset 'asap7 M10 m2 0.5'",
                                  "api: chip.set('pdk','asap7','grid','M10','m2','xoffset','0.5')"
                              ],
                              "help": "Defines the grid offset of a vertical metal layer specified on a per\nstackup and per metal basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid vertical wire offset",
                              "signature": null,
                              "switch": "-pdk_grid_xoffset 'pdkname stackup layer <float>'",
                              "type": "float",
                              "unit": "um",
                              "value": null
                          },
                          "xpitch": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_xpitch 'asap7 M10 m1 0.5'",
                                  "api: chip.set('pdk', 'asap7','grid','M10','m1','xpitch','0.5')"
                              ],
                              "help": "Defines the routing pitch for vertical wires on a per stackup and\nper metal basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid vertical wire pitch",
                              "signature": null,
                              "switch": "-pdk_grid_xpitch 'pdkname stackup layer <float>'",
                              "type": "float",
                              "unit": "um",
                              "value": null
                          },
                          "yoffset": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_yoffset 'asap7 M10 m2 0.5'",
                                  "api: chip.set('pdk', 'asap7','grid','M10','m2','yoffset','0.5')"
                              ],
                              "help": "Defines the grid offset of a horizontal metal layer specified on a per\nstackup and per metal basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid horizontal wire offset",
                              "signature": null,
                              "switch": "-pdk_grid_yoffset 'pdkname stackup layer <float>'",
                              "type": "float",
                              "unit": "um",
                              "value": null
                          },
                          "ypitch": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_ypitch 'asap7 M10 m1 0.5'",
                                  "api: chip.set('pdk','asap7','grid','M10','m1','ypitch','0.5')"
                              ],
                              "help": "Defines the routing pitch for horizontal wires on a per stackup and\nper metal basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid horizontal wire pitch",
                              "signature": null,
                              "switch": "-pdk_grid_ypitch 'pdkname stackup layer <float>'",
                              "type": "float",
                              "unit": "um",
                              "value": null
                          }
                      }
                  }
              },
              "hscribe": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_hscribe 'asap7 0.1'",
                      "api:  chip.set('pdk', 'asap7', 'hscribe', 0.1)"
                  ],
                  "help": "Width of the horizontal scribe line used during die separation.\nThe process is generally completed using a mechanical saw, but can be\ndone through combinations of mechanical saws, lasers, wafer thinning,\nand chemical etching in more advanced technologies. The value is used\nto calculate effective dies per wafer and full factory cost.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: horizontal scribe line width",
                  "signature": null,
                  "switch": "-pdk_hscribe 'pdkname <float>'",
                  "type": "float",
                  "unit": "mm",
                  "value": null
              },
              "layermap": {
                  "default": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_layermap 'asap7 klayout db gds M10 asap7.map'",
                                      "api: chip.set('pdk','asap7','layermap','klayout','db','gds','M10','asap7.map')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Files describing input/output mapping for streaming layout data from\none format to another. A foundry PDK will include an official layer\nlist for all user entered and generated layers supported in the GDS\naccepted by the foundry for processing, but there is no standardized\nlayer definition format that can be read and written by all EDA tools.\nTo ensure mask layer matching, key/value type mapping files are needed\nto convert EDA databases to/from GDS and to convert between different\ntypes of EDA databases. Layer maps are specified on a per metal\nstackup basis. The 'src' and 'dst' can be names of SC supported tools\nor file formats (like 'gds').",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: layer map file",
                                  "signature": [],
                                  "switch": "-pdk_layermap 'pdkname tool src dst stackup <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  }
              },
              "lvs": {
                  "runset": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_lvs_runset 'asap7 magic M10 basic $PDK/lvs.rs'",
                                      "api: chip.set('pdk', 'asap7','lvs','runset','magic','M10','basic','$PDK/lvs.rs')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Runset files for LVS task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: LVS runset files",
                                  "signature": [],
                                  "switch": "-pdk_lvs_runset 'pdkname tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  },
                  "waiver": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_lvs_waiver 'asap7 magic M10 basic $PDK/lvs.txt'",
                                      "api: chip.set('pdk', 'asap7','lvs','waiver','magic','M10','basic','$PDK/lvs.txt')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Waiver files for LVS task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: LVS waiver files",
                                  "signature": [],
                                  "switch": "-pdk_lvs_waiver 'tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  }
              },
              "node": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_node 'asap7 130'",
                      "api:  chip.set('pdk', 'asap7', 'node', 130)"
                  ],
                  "help": "Approximate relative minimum dimension of the process target specified\nin nanometers. The parameter is required for flows and tools that\nleverage the value to drive technology dependent synthesis and APR\noptimization. Node examples include 180, 130, 90, 65, 45, 32, 22 14,\n10, 7, 5, 3.",
                  "lock": "false",
                  "notes": null,
                  "require": "asic",
                  "scope": "global",
                  "shorthelp": "PDK: process node",
                  "signature": null,
                  "switch": "-pdk_node 'pdkname <float>'",
                  "type": "float",
                  "value": null
              },
              "panelsize": {
                  "defvalue": [],
                  "example": [
                      "cli: -pdk_panelsize 'asap7 (45.72,60.96)'",
                      "api:  chip.set('pdk', 'asap7', 'panelsize', (45.72,60.96))"
                  ],
                  "help": "List of panel sizes supported in the manufacturing process.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: panel size",
                  "signature": [],
                  "switch": "-pdk_panelsize 'pdkname <float>'",
                  "type": "[(float,float)]",
                  "unit": "mm",
                  "value": []
              },
              "pexmodel": {
                  "default": {
                      "default": {
                          "default": {
                              "author": [],
                              "copy": "false",
                              "date": [],
                              "defvalue": [],
                              "example": [
                                  "cli: -pdk_pexmodel 'asap7 fastcap M10 max wire.mod'",
                                  "api: chip.set('pdk','asap7','pexmodel','fastcap','M10','max','wire.mod')"
                              ],
                              "filehash": [],
                              "hashalgo": "sha256",
                              "help": "List of filepaths to PDK wire TCAD models used during automated\nsynthesis, APR, and signoff verification. Pexmodels are specified on\na per metal stack basis. Corner values depend on the process being\nused, but typically include nomenclature such as min, max, nominal.\nFor exact names, refer to the DRM. Pexmodels are generally not\nstandardized and specified on a per tool basis. An example of pexmodel\ntype is 'fastcap'.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: parasitic TCAD models",
                              "signature": [],
                              "switch": "-pdk_pexmodel 'pdkname tool stackup corner <file>'",
                              "type": "[file]",
                              "value": []
                          }
                      }
                  }
              },
              "stackup": {
                  "defvalue": [],
                  "example": [
                      "cli: -pdk_stackup 'asap7 2MA4MB2MC'",
                      "api: chip.add('pdk', 'asap7','stackup','2MA4MB2MC')"
                  ],
                  "help": "List of all metal stackups offered in the process node. Older process\nnodes may only offer a single metal stackup, while advanced nodes\noffer a large but finite list of metal stacks with varying combinations\nof metal line pitches and thicknesses. Stackup naming is unique to a\nfoundry, but is generally a long string or code. For example, a 10\nmetal stackup with two 1x wide, four 2x wide, and 4x wide metals,\nmight be identified as 2MA4MB2MC, where MA, MB, and MC denote wiring\nlayers with different properties (thickness, width, space). Each\nstackup will come with its own set of routing technology files and\nparasitic models specified in the pdk_pexmodel and pdk_aprtech\nparameters.",
                  "lock": "false",
                  "notes": null,
                  "require": "asic",
                  "scope": "global",
                  "shorthelp": "PDK: metal stackups",
                  "signature": [],
                  "switch": "-pdk_stackup 'pdkname <str>'",
                  "type": "[str]",
                  "value": []
              },
              "thickness": {
                  "default": {
                      "defvalue": null,
                      "example": [
                          "cli: -pdk_thickness 'asap7 2MA4MB2MC 1.57'",
                          "api:  chip.set('pdk', 'asap7', 'thickness', '2MA4MB2MC', 1.57)"
                      ],
                      "help": "Thickness of a manfuatured unit specified on a per stackup basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: unit thickness",
                      "signature": null,
                      "switch": "-pdk_thickness 'pdkname stackup <float>'",
                      "type": "float",
                      "unit": "mm",
                      "value": null
                  }
              },
              "unitcost": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_unitcost 'asap7 10000'",
                      "api:  chip.set('pdk', 'asap7', 'unitcost', 10000)"
                  ],
                  "help": "Raw cost per unit shipped by the factory, not accounting for yield\nloss.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: unit cost",
                  "signature": null,
                  "switch": "-pdk_unitcost 'pdkname <float>'",
                  "type": "float",
                  "unit": "USD",
                  "value": null
              },
              "var": {
                  "default": {
                      "default": {
                          "default": {
                              "defvalue": [],
                              "example": [
                                  "cli: -pdk_var 'asap7 xyce modeltype M10 bsim4'",
                                  "api: chip.set('pdk','asap7','var','xyce','modeltype','M10','bsim4')"
                              ],
                              "help": "List of key/value strings specified on a per tool and per stackup basis.\nThe parameter should only be used for specifying variables that are\nnot directly  supported by the SiliconCompiler PDK schema.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: special variable",
                              "signature": [],
                              "switch": "-pdk_var 'pdkname tool stackup key <str>'",
                              "type": "[str]",
                              "value": []
                          }
                      }
                  }
              },
              "version": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_version 'asap7 1.0'",
                      "api:  chip.set('pdk', 'asap7', 'version', '1.0')"
                  ],
                  "help": "Alphanumeric string specifying the version of the PDK. Verification of\ncorrect PDK and IP versions is a hard ASIC tapeout require in all\ncommercial foundries. The version number can be used for design manifest\ntracking and tapeout checklists.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: version",
                  "signature": null,
                  "switch": "-pdk_version 'pdkname <str>'",
                  "type": "str",
                  "value": null
              },
              "vscribe": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_vscribe 'asap7 0.1'",
                      "api:  chip.set('pdk', 'asap7', 'vscribe', 0.1)"
                  ],
                  "help": " Width of the vertical scribe line used during die separation.\nThe process is generally completed using a mechanical saw, but can be\ndone through combinations of mechanical saws, lasers, wafer thinning,\nand chemical etching in more advanced technologies. The value is used\nto calculate effective dies per wafer and full factory cost.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: vertical scribe line width",
                  "signature": null,
                  "switch": "-pdk_vscribe 'pdkname <float>'",
                  "type": "float",
                  "unit": "mm",
                  "value": null
              },
              "wafersize": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_wafersize 'asap7 300'",
                      "api:  chip.set('pdk', 'asap7', 'wafersize', 300)"
                  ],
                  "help": "Wafer diameter used in wafer based manufacturing process. The standard diameter\nfor leading edge manufacturing is 300mm. For older process technologies\nand specialty fabs, smaller diameters such as 200, 100, 125, 100 are common.\nThe value is used to calculate dies per wafer and full factory chip costs.",
                  "lock": "false",
                  "notes": null,
                  "require": "asic",
                  "scope": "global",
                  "shorthelp": "PDK: wafer size",
                  "signature": null,
                  "switch": "-pdk_wafersize 'pdkname <float>'",
                  "type": "float",
                  "unit": "mm",
                  "value": null
              }
          }
      },
      "record": {
          "default": {
              "default": {
                  "arch": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_arch 'dfm 0 <x86_64>'",
                          "api: chip.set('record','dfm','0','arch', <x86_64>)"
                      ],
                      "help": "Record tracking the hardware architecture per step and index basis. (x86_64, rv64imafdc)",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: hardware architecture",
                      "signature": null,
                      "switch": "-record_arch 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "distro": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_distro 'dfm 0 <ubuntu>'",
                          "api: chip.set('record','dfm','0','distro', <ubuntu>)"
                      ],
                      "help": "Record tracking the distro name per step and index basis. (ubuntu, redhat, centos)",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: distro name",
                      "signature": null,
                      "switch": "-record_distro 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "endtime": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_endtime 'dfm 0 <2021-09-06 12:20:20>'",
                          "api: chip.set('record','dfm','0','endtime', <2021-09-06 12:20:20>)"
                      ],
                      "help": "Record tracking the end time per step and index basis. Time is reported in the ISO 8601 format YYYY-MM-DD HR:MIN:SEC",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: end time",
                      "signature": null,
                      "switch": "-record_endtime 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "ipaddr": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_ipaddr 'dfm 0 <<addr>>'",
                          "api: chip.set('record','dfm','0','ipaddr', <<addr>>)"
                      ],
                      "help": "Record tracking the IP address per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: IP address",
                      "signature": null,
                      "switch": "-record_ipaddr 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "kernelversion": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_kernelversion 'dfm 0 <5.11.0-34-generic>'",
                          "api: chip.set('record','dfm','0','kernelversion', <5.11.0-34-generic>)"
                      ],
                      "help": "Record tracking the O/S kernel version per step and index basis. Used for platforms that support a distinction\nbetween os kernels and os distributions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: O/S kernel version",
                      "signature": null,
                      "switch": "-record_kernelversion 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "macaddr": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_macaddr 'dfm 0 <<addr>>'",
                          "api: chip.set('record','dfm','0','macaddr', <<addr>>)"
                      ],
                      "help": "Record tracking the MAC address per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: MAC address",
                      "signature": null,
                      "switch": "-record_macaddr 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "machine": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_machine 'dfm 0 <carbon>'",
                          "api: chip.set('record','dfm','0','machine', <carbon>)"
                      ],
                      "help": "Record tracking the machine name per step and index basis. (myhost, localhost, ...",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: machine name",
                      "signature": null,
                      "switch": "-record_machine 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "osversion": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_osversion 'dfm 0 <20.04.1-Ubuntu>'",
                          "api: chip.set('record','dfm','0','osversion', <20.04.1-Ubuntu>)"
                      ],
                      "help": "Record tracking the O/S version per step and index basis. Since there is not standard version system for operating\nsystems, extracting information from is platform dependent.\nFor Linux based operating systems, the 'osversion' is the\nversion of the distro.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: O/S version",
                      "signature": null,
                      "switch": "-record_osversion 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "platform": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_platform 'dfm 0 <linux>'",
                          "api: chip.set('record','dfm','0','platform', <linux>)"
                      ],
                      "help": "Record tracking the platform name per step and index basis. (linux, windows, freebsd)",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: platform name",
                      "signature": null,
                      "switch": "-record_platform 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "publickey": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_publickey 'dfm 0 <<key>>'",
                          "api: chip.set('record','dfm','0','publickey', <<key>>)"
                      ],
                      "help": "Record tracking the public key per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: public key",
                      "signature": null,
                      "switch": "-record_publickey 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "region": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_region 'dfm 0 <US Gov Boston>'",
                          "api: chip.set('record','dfm','0','region', <US Gov Boston>)"
                      ],
                      "help": "Record tracking the cloud region per step and index basis. Recommended naming methodology:\n\n* local: node is the local machine\n* onprem: node in on-premises IT infrastructure\n* public: generic public cloud\n* govcloud: generic US government cloud\n* <region>: cloud and entity specific region string name",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: cloud region",
                      "signature": null,
                      "switch": "-record_region 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "scversion": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_scversion 'dfm 0 <1.0>'",
                          "api: chip.set('record','dfm','0','scversion', <1.0>)"
                      ],
                      "help": "Record tracking the software version per step and index basis. Version number for the SiliconCompiler software.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: software version",
                      "signature": null,
                      "switch": "-record_scversion 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "starttime": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_starttime 'dfm 0 <2021-09-06 12:20:20>'",
                          "api: chip.set('record','dfm','0','starttime', <2021-09-06 12:20:20>)"
                      ],
                      "help": "Record tracking the start time per step and index basis. Time is reported in the ISO 8601 format YYYY-MM-DD HR:MIN:SEC",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: start time",
                      "signature": null,
                      "switch": "-record_starttime 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "toolargs": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_toolargs 'dfm 0 <-I include/ foo.v>'",
                          "api: chip.set('record','dfm','0','toolargs', <-I include/ foo.v>)"
                      ],
                      "help": "Record tracking the tool CLI arguments per step and index basis. Arguments passed to tool via CLI.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: tool CLI arguments",
                      "signature": null,
                      "switch": "-record_toolargs 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "toolpath": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_toolpath 'dfm 0 </usr/bin/openroad>'",
                          "api: chip.set('record','dfm','0','toolpath', </usr/bin/openroad>)"
                      ],
                      "help": "Record tracking the tool path per step and index basis. Full path to tool executable used to run this\ntask.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: tool path",
                      "signature": null,
                      "switch": "-record_toolpath 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "toolversion": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_toolversion 'dfm 0 <1.0>'",
                          "api: chip.set('record','dfm','0','toolversion', <1.0>)"
                      ],
                      "help": "Record tracking the tool version per step and index basis. The tool version captured correspnds to the 'tool'\nparameter within the 'eda' dictionary.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: tool version",
                      "signature": null,
                      "switch": "-record_toolversion 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "userid": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_userid 'dfm 0 <wiley>'",
                          "api: chip.set('record','dfm','0','userid', <wiley>)"
                      ],
                      "help": "Record tracking the userid per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: userid",
                      "signature": null,
                      "switch": "-record_userid 'step index <str>'",
                      "type": "str",
                      "value": null
                  }
              }
          }
      },
      "schemaversion": {
          "defvalue": "0.8.0",
          "example": [
              "api: chip.get('schemaversion')"
          ],
          "help": "SiliconCompiler schema version number.",
          "lock": "true",
          "notes": null,
          "require": "all",
          "scope": "global",
          "shorthelp": "Schema version number",
          "signature": null,
          "switch": "-schemaversion <str>",
          "type": "str",
          "value": "0.8.0"
      },
      "tool": {
          "default": {
              "continue": {
                  "defvalue": "false",
                  "example": [
                      "cli: -tool_continue 'verilator true'",
                      "api: chip.set('tool','verilator','continue', true)"
                  ],
                  "help": "Directs tool to continue operating even if errors are\nencountered.",
                  "lock": "false",
                  "notes": null,
                  "require": "all",
                  "scope": "job",
                  "shorthelp": "Tool: continue-on-error option",
                  "signature": null,
                  "switch": "-tool_continue 'tool <bool>'",
                  "type": "bool",
                  "value": "false"
              },
              "env": {
                  "default": {
                      "default": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -tool_env 'openroad cts 0 MYVAR 42'",
                                  "api: chip.set('tool','openroad','env','cts','0','MYVAR','42')"
                              ],
                              "help": "Environment variables to set for individual tasks. Keys and values\nshould be set in accordance with the tool's documentation. Most\ntools do not require extra environment variables to function.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: environment variables",
                              "signature": null,
                              "switch": "-tool_env 'tool step index name <str>'",
                              "type": "str",
                              "value": null
                          }
                      }
                  }
              },
              "exe": {
                  "defvalue": null,
                  "example": [
                      "cli: -tool_exe 'openroad openroad'",
                      "api:  chip.set('tool','openroad','exe','openroad')"
                  ],
                  "help": "Tool executable name.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: executable name",
                  "signature": null,
                  "switch": "-tool_exe 'tool <str>'",
                  "type": "str",
                  "value": null
              },
              "format": {
                  "defvalue": null,
                  "example": [
                      "cli: -tool_format 'yosys tcl'",
                      "api: chip.set('tool','yosys','format','tcl')"
                  ],
                  "help": "File format for tool manifest handoff. Supported formats are tcl,\nyaml, and json.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: manifest file format",
                  "signature": null,
                  "switch": "-tool_format 'tool <file>'",
                  "type": "str",
                  "value": null
              },
              "input": {
                  "default": {
                      "default": {
                          "author": [],
                          "copy": "false",
                          "date": [],
                          "defvalue": [],
                          "example": [
                              "cli: -tool_input 'openroad place 0 oh_add.def'",
                              "api: chip.set('tool','openroad','input','place','0','oh_add.def')"
                          ],
                          "filehash": [],
                          "hashalgo": "sha256",
                          "help": "List of data files to be copied from previous flowgraph steps 'output'\ndirectory. The list of steps to copy files from is defined by the\nlist defined by the dictionary key ['flowgraph', step, index, 'input'].\nAll files must be available for flow to continue. If a file\nis missing, the program exists on an error.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: input files",
                          "signature": [],
                          "switch": "-tool_input 'tool step index <str>'",
                          "type": "[file]",
                          "value": []
                      }
                  }
              },
              "keep": {
                  "default": {
                      "default": {
                          "defvalue": [],
                          "example": [
                              "cli: -tool_keep 'surelog import 0 slp_all'",
                              "api: chip.set('tool','surelog','script','import','0','slpp_all')"
                          ],
                          "help": "Names of additional files and directories in the work directory that\nshould be kept when :keypath:`option, clean` is true.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: files to keep",
                          "signature": [],
                          "switch": "-tool_keep 'tool step index <str>'",
                          "type": "[str]",
                          "value": []
                      }
                  }
              },
              "licenseserver": {
                  "default": {
                      "defvalue": [],
                      "example": [
                          "cli: -tool_licenseserver 'atool ACME_LICENSE 1700@server'",
                          "api: chip.set('tool','atool','licenseserver','ACME_LICENSE','1700@server')"
                      ],
                      "help": "Defines a set of tool specific environment variables used by the executables\nthat depend on license key servers to control access. For multiple servers,\nseparate each server by a 'colon'. The named license variable are read at\nruntime (run()) and the environment variables are set.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Tool: license servers",
                      "signature": [],
                      "switch": "-tool_licenseserver 'name key <str>'",
                      "type": "[str]",
                      "value": []
                  }
              },
              "option": {
                  "default": {
                      "default": {
                          "defvalue": [],
                          "example": [
                              "cli: -tool_option 'openroad cts 0 -no_init'",
                              "api: chip.set('tool','openroad','option','cts','0','-no_init')"
                          ],
                          "help": "List of command line options for the tool executable, specified on\na per tool and per step basis. Options must not include spaces.\nFor multiple argument options, each option is a separate list element.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: executable options",
                          "signature": [],
                          "switch": "-tool_option 'tool step index <str>'",
                          "type": "[str]",
                          "value": []
                      }
                  }
              },
              "output": {
                  "default": {
                      "default": {
                          "author": [],
                          "copy": "false",
                          "date": [],
                          "defvalue": [],
                          "example": [
                              "cli: -tool_output 'openroad place 0 oh_add.def'",
                              "api: chip.set('tool','openroad','output','place','0','oh_add.def')"
                          ],
                          "filehash": [],
                          "hashalgo": "sha256",
                          "help": "List of data files to be copied from previous flowgraph steps 'output'\ndirectory. The list of steps to copy files from is defined by the\nlist defined by the dictionary key ['flowgraph', step, index, 'output'].\nAll files must be available for flow to continue. If a file\nis missing, the program exists on an error.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: output files",
                          "signature": [],
                          "switch": "-tool_output 'tool step index <str>'",
                          "type": "[file]",
                          "value": []
                      }
                  }
              },
              "path": {
                  "defvalue": null,
                  "example": [
                      "cli: -tool_path 'openroad /usr/local/bin'",
                      "api:  chip.set('tool','openroad','path','/usr/local/bin')"
                  ],
                  "help": "File system path to tool executable. The path is prepended to the\nsystem PATH environment variable for batch and interactive runs. The\npath parameter can be left blank if the 'exe' is already in the\nenvironment search path.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: executable path",
                  "signature": null,
                  "switch": "-tool_path 'tool <dir>'",
                  "type": "dir",
                  "value": null
              },
              "refdir": {
                  "default": {
                      "default": {
                          "defvalue": [],
                          "example": [
                              "cli: -tool_refdir 'yosys syn 0 ./myref'",
                              "api:  chip.set('tool','yosys','refdir','syn','0','./myref')"
                          ],
                          "help": "Path to directories containing reference flow scripts, specified\non a per step and index basis.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: script directory",
                          "signature": [],
                          "switch": "-tool_refdir 'tool step index <dir>'",
                          "type": "[dir]",
                          "value": []
                      }
                  }
              },
              "regex": {
                  "default": {
                      "default": {
                          "default": {
                              "defvalue": [],
                              "example": [
                                  "cli: -tool_regex 'openroad place 0 errors -v ERROR'",
                                  "api: chip.set('tool','openroad','regex','place','0','errors','-v ERROR')"
                              ],
                              "help": "A list of piped together grep commands. Each entry represents a set\nof command line arguments for grep including the regex pattern to\nmatch. Starting with the first list entry, each grep output is piped\ninto the following grep command in the list. Supported grep options\ninclude ``-v`` and ``-e``. Patterns starting with \"-\" should be\ndirectly preceeded by the ``-e`` option. The following example\nillustrates the concept.\n\nUNIX grep:\n\n.. code-block:: bash\n\n    $ grep WARNING place.log | grep -v \"bbox\" > place.warnings\n\nSiliconCompiler::\n\n    chip.set('tool', 'openroad', 'regex', 'place', '0', 'warnings', [\"WARNING\", \"-v bbox\"])\n\nThe \"errors\" and \"warnings\" suffixes are special cases. When set,\nthe number of matches found for these regexes will be used to set\nthe errors and warnings metrics for the task, respectively.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: regex filter",
                              "signature": [],
                              "switch": "-tool_regex 'tool step index suffix <str>'",
                              "type": "[str]",
                              "value": []
                          }
                      }
                  }
              },
              "report": {
                  "default": {
                      "default": {
                          "default": {
                              "author": [],
                              "copy": "false",
                              "date": [],
                              "defvalue": [],
                              "example": [
                                  "cli: -tool_report 'openroad place 0 holdtns place.log'",
                                  "api: chip.set('tool','openroad','report','syn','0','holdtns','place.log')"
                              ],
                              "filehash": [],
                              "hashalgo": "sha256",
                              "help": "List of report files associated with a specific 'metric'. The file path\nspecified is relative to the run directory of the current task.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: report files",
                              "signature": [],
                              "switch": "-tool_report 'tool step index metric <str>'",
                              "type": "[file]",
                              "value": []
                          }
                      }
                  }
              },
              "require": {
                  "default": {
                      "default": {
                          "defvalue": [],
                          "example": [
                              "cli: -tool_require 'openroad cts 0 design'",
                              "api: chip.set('tool','openroad','require','cts','0','design')"
                          ],
                          "help": "List of keypaths to required tool parameters. The list is used\nby check() to verify that all parameters have been set up before\nstep execution begins.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: parameter requirements",
                          "signature": [],
                          "switch": "-tool_require 'tool step index <str>'",
                          "type": "[str]",
                          "value": []
                      }
                  }
              },
              "script": {
                  "default": {
                      "default": {
                          "author": [],
                          "copy": "false",
                          "date": [],
                          "defvalue": [],
                          "example": [
                              "cli: -tool_script 'yosys syn 0 syn.tcl'",
                              "api: chip.set('tool','yosys','script','syn','0','syn.tcl')"
                          ],
                          "filehash": [],
                          "hashalgo": "sha256",
                          "help": "Path to the entry script called by the executable specified\non a per tool and per step basis.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: entry script",
                          "signature": [],
                          "switch": "-tool_script 'tool step index <file>'",
                          "type": "[file]",
                          "value": []
                      }
                  }
              },
              "stderr": {
                  "default": {
                      "default": {
                          "destination": {
                              "defvalue": "log",
                              "example": [
                                  "cli: -tool_stderr_destination 'ghdl import 0 log'",
                                  "api: chip.set('tool','ghdl','stderr','import','0','destination','log')"
                              ],
                              "help": "Defines where to direct the output generated over stderr.\nSupported options are:\nnone: the stream generated to STDERR is ignored\nlog: the generated stream is stored in <step>.<suffix>; if not in quiet mode,\nit is additionally dumped to the display output: the generated stream is\nstored in outputs/<design>.<suffix>",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: Destination for stderr",
                              "signature": null,
                              "switch": "-tool_stderr_destination 'tool step index [log|output|none]'",
                              "type": "str",
                              "value": "log"
                          },
                          "suffix": {
                              "defvalue": "log",
                              "example": [
                                  "cli: -tool_stderr_suffix 'ghdl import 0 log'",
                                  "api: chip.set('tool','ghdl','stderr','import','0','suffix','log')"
                              ],
                              "help": "Specifies the file extension for the content redirected from stderr.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: File suffix for redirected stderr",
                              "signature": null,
                              "switch": "-tool_stderr_suffix 'tool step index <str>'",
                              "type": "str",
                              "value": "log"
                          }
                      }
                  }
              },
              "stdout": {
                  "default": {
                      "default": {
                          "destination": {
                              "defvalue": "log",
                              "example": [
                                  "cli: -tool_stdout_destination 'ghdl import 0 log'",
                                  "api: chip.set('tool','ghdl','stdout','import','0','destination','log')"
                              ],
                              "help": "Defines where to direct the output generated over stdout.\nSupported options are:\nnone: the stream generated to STDOUT is ignored\nlog: the generated stream is stored in <step>.<suffix>; if not in quiet mode,\nit is additionally dumped to the display output: the generated stream is stored\nin outputs/<design>.<suffix>",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: Destination for stdout",
                              "signature": null,
                              "switch": "-tool_stdout_destination 'tool step index [log|output|none]'",
                              "type": "str",
                              "value": "log"
                          },
                          "suffix": {
                              "defvalue": "log",
                              "example": [
                                  "cli: -tool_stdout_suffix 'ghdl import 0 log'",
                                  "api: chip.set('tool','ghdl','stdout','import','0','suffix','log')"
                              ],
                              "help": "Specifies the file extension for the content redirected from stdout.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: File suffix for redirected stdout",
                              "signature": null,
                              "switch": "-tool_stdout_suffix 'tool step index <str>'",
                              "type": "str",
                              "value": "log"
                          }
                      }
                  }
              },
              "threads": {
                  "default": {
                      "default": {
                          "defvalue": null,
                          "example": [
                              "cli: -tool_threads 'magic drc 0 64'",
                              "api: chip.set('tool','magic','threads','drc','0','64')"
                          ],
                          "help": "Thread parallelism to use for execution specified on a per tool and per\nstep basis. If not specified, SC queries the operating system and sets\nthe threads based on the maximum thread count supported by the\nhardware.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: thread parallelism",
                          "signature": null,
                          "switch": "-tool_threads 'tool step index <int>'",
                          "type": "int",
                          "value": null
                      }
                  }
              },
              "var": {
                  "default": {
                      "default": {
                          "default": {
                              "defvalue": [],
                              "example": [
                                  "cli: -tool_variable 'openroad cts 0 myvar 42'",
                                  "api: chip.set('tool','openroad','var','cts','0','myvar','42')"
                              ],
                              "help": "Tool script variables specified as key value pairs. Variable\nnames and value types must match the name and type of tool and reference\nscript consuming the variable.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: script variables",
                              "signature": [],
                              "switch": "-tool_variable 'tool step index key <str>'",
                              "type": "[str]",
                              "value": []
                          }
                      }
                  }
              },
              "vendor": {
                  "defvalue": null,
                  "example": [
                      "cli: -tool_vendor 'yosys yosys'",
                      "api: chip.set('tool','yosys','vendor','yosys')"
                  ],
                  "help": "Name of the tool vendor. Parameter can be used to set vendor\nspecific technology variables in the PDK and libraries. For\nopen source projects, the project name should be used in\nplace of vendor.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: vendor",
                  "signature": null,
                  "switch": "-tool_vendor 'tool <str>'",
                  "type": "str",
                  "value": null
              },
              "version": {
                  "defvalue": [],
                  "example": [
                      "cli: -tool_version 'openroad >=v2.0'",
                      "api:  chip.set('tool','openroad','version','>=v2.0')"
                  ],
                  "help": "List of acceptable versions of the tool executable to be used. Each\nentry in this list must be a version specifier as described by Python\n`PEP-440 <https://peps.python.org/pep-0440/#version-specifiers>`_.\nDuring task execution, the tool is called with the 'vswitch' to\ncheck the runtime executable version. If the version of the system\nexecutable is not allowed by any of the specifiers in 'version',\nthen the job is halted pre-execution. For backwards compatibility,\nentries that do not conform to the standard will be interpreted as a\nversion with an '==' specifier. This check can be disabled by\nsetting 'novercheck' to True.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: version",
                  "signature": [],
                  "switch": "-tool_version 'tool <str>'",
                  "type": "[str]",
                  "value": []
              },
              "vswitch": {
                  "defvalue": [],
                  "example": [
                      "cli: -tool_vswitch 'openroad -version'",
                      "api:  chip.set('tool','openroad','vswitch','-version')"
                  ],
                  "help": "Command line switch to use with executable used to print out\nthe version number. Common switches include -v, -version,\n--version. Some tools may require extra flags to run in batch mode.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: executable version switch",
                  "signature": [],
                  "switch": "-tool_vswitch 'tool <str>'",
                  "type": "[str]",
                  "value": []
              },
              "warningoff": {
                  "defvalue": [],
                  "example": [
                      "cli: -tool_warningoff 'verilator COMBDLY'",
                      "api: chip.set('tool','verilator','warningoff','COMBDLY')"
                  ],
                  "help": "A list of EDA warnings for which printing should be suppressed.\nGenerally this is done on a per design basis after review has\ndetermined that warning can be safely ignored The code for turning\noff warnings can be found in the specific tool reference manual.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: warning filter",
                  "signature": [],
                  "switch": "-tool_warningoff 'tool <str>'",
                  "type": "[str]",
                  "value": []
              }
          }
      },
      "unit": {
          "capacitance": {
              "defvalue": "pf",
              "example": [
                  "cli: -unit_capacitance 'pf'",
                  "api: chip.set('unit','capacitance',pf)"
              ],
              "help": "Units used for capacitance when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: capacitance",
              "signature": null,
              "switch": "-unit_capacitance '<str>'",
              "type": "str",
              "value": "pf"
          },
          "current": {
              "defvalue": "ma",
              "example": [
                  "cli: -unit_current 'ma'",
                  "api: chip.set('unit','current',ma)"
              ],
              "help": "Units used for current when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: current",
              "signature": null,
              "switch": "-unit_current '<str>'",
              "type": "str",
              "value": "ma"
          },
          "energy": {
              "defvalue": "pj",
              "example": [
                  "cli: -unit_energy 'pj'",
                  "api: chip.set('unit','energy',pj)"
              ],
              "help": "Units used for energy when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: energy",
              "signature": null,
              "switch": "-unit_energy '<str>'",
              "type": "str",
              "value": "pj"
          },
          "inductance": {
              "defvalue": "nh",
              "example": [
                  "cli: -unit_inductance 'nh'",
                  "api: chip.set('unit','inductance',nh)"
              ],
              "help": "Units used for inductance when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: inductance",
              "signature": null,
              "switch": "-unit_inductance '<str>'",
              "type": "str",
              "value": "nh"
          },
          "length": {
              "defvalue": "um",
              "example": [
                  "cli: -unit_length 'um'",
                  "api: chip.set('unit','length',um)"
              ],
              "help": "Units used for length when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: length",
              "signature": null,
              "switch": "-unit_length '<str>'",
              "type": "str",
              "value": "um"
          },
          "mass": {
              "defvalue": "g",
              "example": [
                  "cli: -unit_mass 'g'",
                  "api: chip.set('unit','mass',g)"
              ],
              "help": "Units used for mass when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: mass",
              "signature": null,
              "switch": "-unit_mass '<str>'",
              "type": "str",
              "value": "g"
          },
          "power": {
              "defvalue": "mw",
              "example": [
                  "cli: -unit_power 'mw'",
                  "api: chip.set('unit','power',mw)"
              ],
              "help": "Units used for power when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: power",
              "signature": null,
              "switch": "-unit_power '<str>'",
              "type": "str",
              "value": "mw"
          },
          "resistance": {
              "defvalue": "ohm",
              "example": [
                  "cli: -unit_resistance 'ohm'",
                  "api: chip.set('unit','resistance',ohm)"
              ],
              "help": "Units used for resistance when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: resistance",
              "signature": null,
              "switch": "-unit_resistance '<str>'",
              "type": "str",
              "value": "ohm"
          },
          "time": {
              "defvalue": "ns",
              "example": [
                  "cli: -unit_time 'ns'",
                  "api: chip.set('unit','time',ns)"
              ],
              "help": "Units used for time when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: time",
              "signature": null,
              "switch": "-unit_time '<str>'",
              "type": "str",
              "value": "ns"
          },
          "voltage": {
              "defvalue": "mv",
              "example": [
                  "cli: -unit_voltage 'mv'",
                  "api: chip.set('unit','voltage',mv)"
              ],
              "help": "Units used for voltage when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: voltage",
              "signature": null,
              "switch": "-unit_voltage '<str>'",
              "type": "str",
              "value": "mv"
          }
      }
  }
  
files: tests/core/data/defaults.json
files: {
      "arg": {
          "flow": {
              "default": {
                  "defvalue": [],
                  "example": [
                      "cli: -arg_flow 'n 100'",
                      "api: chip.set('arg','flow','n', 100)"
                  ],
                  "help": "Parameter passed in as key/value pair to the flow target\nreferenced in the load_flow() API call. See the target flow\nfor specific guidelines regarding configuration parameters.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "scratch",
                  "shorthelp": "ARG: Flow argument",
                  "signature": [],
                  "switch": "-arg_flow 'key <str>'",
                  "type": "[str]",
                  "value": []
              }
          },
          "index": {
              "defvalue": null,
              "example": [
                  "cli: -arg_index 0",
                  "api: chip.set('arg','index','0')"
              ],
              "help": "Dynamic parameter passed in by the sc runtime as an argument to\na runtime task. The parameter enables configuration code\n(usually TCL) to use control flow that depend on the current\n'index'. The parameter is used the run() function and\nis not intended for external use.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "scratch",
              "shorthelp": "ARG: Index argument",
              "signature": null,
              "switch": "-arg_index <str>",
              "type": "str",
              "value": null
          },
          "pdk": {
              "default": {
                  "defvalue": [],
                  "example": [
                      "cli: -arg_pdk 'mimcap true'",
                      "api: chip.set('arg','pdk','mimcap','true')"
                  ],
                  "help": "Parameter passed in as key/value pair to the technology target\nreferenced in the load_pdk() API call. See the target technology\nfor specific guidelines regarding configuration parameters.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "scratch",
                  "shorthelp": "ARG: PDK argument",
                  "signature": [],
                  "switch": "-arg_pdk 'key <str>",
                  "type": "[str]",
                  "value": []
              }
          },
          "step": {
              "defvalue": null,
              "example": [
                  "cli: -arg_step 'route'",
                  "api: chip.set('arg', 'step', 'route')"
              ],
              "help": "Dynamic parameter passed in by the sc runtime as an argument to\na runtime task. The parameter enables configuration code\n(usually TCL) to use control flow that depend on the current\n'step'. The parameter is used the run() function and\nis not intended for external use.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "scratch",
              "shorthelp": "ARG: Step argument",
              "signature": null,
              "switch": "-arg_step <str>",
              "type": "str",
              "value": null
          }
      },
      "asic": {
          "aspectratio": {
              "defvalue": null,
              "example": [
                  "cli: -asic_aspectratio 2.0",
                  "api: chip.set('asic', 'aspectratio', '2.0')"
              ],
              "help": "Height to width ratio of the block for automated floor-planning.\nValues below 0.1 and above 10 should be avoided as they will likely fail\nto converge during placement and routing. The ideal aspect ratio for\nmost designs is 1. This value is only used when no diearea or floorplan\nis supplied.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: block aspect ratio",
              "signature": null,
              "switch": "-asic_aspectratio <float>",
              "type": "float",
              "value": null
          },
          "cells": {
              "antenna": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_antenna '*eco*'",
                      "api: chip.set('asic','cells',antenna,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: antenna cell list",
                  "signature": [],
                  "switch": "-asic_cells_antenna '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "buf": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_buf '*eco*'",
                      "api: chip.set('asic','cells',buf,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: buf cell list",
                  "signature": [],
                  "switch": "-asic_cells_buf '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "clkbuf": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_clkbuf '*eco*'",
                      "api: chip.set('asic','cells',clkbuf,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: clkbuf cell list",
                  "signature": [],
                  "switch": "-asic_cells_clkbuf '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "clkdelay": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_clkdelay '*eco*'",
                      "api: chip.set('asic','cells',clkdelay,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: clkdelay cell list",
                  "signature": [],
                  "switch": "-asic_cells_clkdelay '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "clkgate": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_clkgate '*eco*'",
                      "api: chip.set('asic','cells',clkgate,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: clkgate cell list",
                  "signature": [],
                  "switch": "-asic_cells_clkgate '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "clkicg": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_clkicg '*eco*'",
                      "api: chip.set('asic','cells',clkicg,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: clkicg cell list",
                  "signature": [],
                  "switch": "-asic_cells_clkicg '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "clkinv": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_clkinv '*eco*'",
                      "api: chip.set('asic','cells',clkinv,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: clkinv cell list",
                  "signature": [],
                  "switch": "-asic_cells_clkinv '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "clklogic": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_clklogic '*eco*'",
                      "api: chip.set('asic','cells',clklogic,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: clklogic cell list",
                  "signature": [],
                  "switch": "-asic_cells_clklogic '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "decap": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_decap '*eco*'",
                      "api: chip.set('asic','cells',decap,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: decap cell list",
                  "signature": [],
                  "switch": "-asic_cells_decap '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "delay": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_delay '*eco*'",
                      "api: chip.set('asic','cells',delay,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: delay cell list",
                  "signature": [],
                  "switch": "-asic_cells_delay '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "driver": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_driver '*eco*'",
                      "api: chip.set('asic','cells',driver,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: driver cell list",
                  "signature": [],
                  "switch": "-asic_cells_driver '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "endcap": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_endcap '*eco*'",
                      "api: chip.set('asic','cells',endcap,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: endcap cell list",
                  "signature": [],
                  "switch": "-asic_cells_endcap '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "filler": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_filler '*eco*'",
                      "api: chip.set('asic','cells',filler,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: filler cell list",
                  "signature": [],
                  "switch": "-asic_cells_filler '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "hold": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_hold '*eco*'",
                      "api: chip.set('asic','cells',hold,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: hold cell list",
                  "signature": [],
                  "switch": "-asic_cells_hold '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "ignore": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_ignore '*eco*'",
                      "api: chip.set('asic','cells',ignore,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: ignore cell list",
                  "signature": [],
                  "switch": "-asic_cells_ignore '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "load": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_load '*eco*'",
                      "api: chip.set('asic','cells',load,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: load cell list",
                  "signature": [],
                  "switch": "-asic_cells_load '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "tap": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_tap '*eco*'",
                      "api: chip.set('asic','cells',tap,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: tap cell list",
                  "signature": [],
                  "switch": "-asic_cells_tap '<str>'",
                  "type": "[str]",
                  "value": []
              },
              "tie": {
                  "defvalue": [],
                  "example": [
                      "cli: -asic_cells_tie '*eco*'",
                      "api: chip.set('asic','cells',tie,'*eco*')"
                  ],
                  "help": "List of cells grouped by a property that can be accessed\ndirectly by the designer and tools. The example below shows how\nall cells containing the string 'eco' could be marked as dont use\nfor the tool.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: tie cell list",
                  "signature": [],
                  "switch": "-asic_cells_tie '<str>'",
                  "type": "[str]",
                  "value": []
              }
          },
          "corearea": {
              "defvalue": [],
              "example": [
                  "cli: -asic_corearea '(0,0)'",
                  "api: chip.set('asic', 'corearea', (0,0))"
              ],
              "help": "List of (x,y) points that define the outline of the core area for the\nphysical design. Simple rectangle areas can be defined with two points,\none for the lower left corner and one for the upper right corner. All\nvalues are specified in microns.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: core area outline",
              "signature": [],
              "switch": "-asic_corearea <[(float,float)]>",
              "type": "[(float,float)]",
              "unit": "um",
              "value": []
          },
          "coremargin": {
              "defvalue": null,
              "example": [
                  "cli: -asic_coremargin 1",
                  "api: chip.set('asic', 'coremargin', '1')"
              ],
              "help": "Halo/margin between the die boundary and core placement for\nautomated floorplanning when no diearea or floorplan is\nsupplied.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: block core margin",
              "signature": null,
              "switch": "-asic_coremargin <float>",
              "type": "float",
              "unit": "um",
              "value": null
          },
          "delaymodel": {
              "defvalue": null,
              "example": [
                  "cli: -asic_delaymodel ccs",
                  "api: chip.set('asic', 'delaymodel', 'ccs')"
              ],
              "help": "Delay model to use for the target libs. Supported values\nare nldm and ccs.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: delay model",
              "signature": null,
              "switch": "-asic_delaymodel <str>",
              "type": "str",
              "value": null
          },
          "density": {
              "defvalue": null,
              "example": [
                  "cli: -asic_density 30",
                  "api: chip.set('asic', 'density', '30')"
              ],
              "help": "Target density based on the total design cell area reported\nafter synthesis. This number is used when no diearea or floorplan is\nsupplied. Any number between 1 and 100 is legal, but values above 50\nmay fail due to area/congestion issues during apr.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: target core density",
              "signature": null,
              "switch": "-asic_density <float>",
              "type": "float",
              "value": null
          },
          "diearea": {
              "defvalue": [],
              "example": [
                  "cli: -asic_diearea '(0,0)'",
                  "api: chip.set('asic', 'diearea', (0,0))"
              ],
              "help": "List of (x,y) points that define the outline of the die area for the\nphysical design. Simple rectangle areas can be defined with two points,\none for the lower left corner and one for the upper right corner. All\nvalues are specified in microns.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: die area outline",
              "signature": [],
              "switch": "-asic_diearea <[(float,float)]>",
              "type": "[(float,float)]",
              "unit": "um",
              "value": []
          },
          "dir": {
              "default": {
                  "default": {
                      "defvalue": [],
                      "example": [
                          "cli: -asic_dir 'lib atool db ~/libdb'",
                          "api: chip.set('asic','dir','atool','db','~/libdb')"
                      ],
                      "help": "List of named dirs specified on a per tool basis. The parameter\nshould only be used for specifying dirs that are not directly\nsupported by the ASIC schema.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "ASIC: special directory",
                      "signature": [],
                      "switch": "-asic_dir 'tool key <dir>'",
                      "type": "[dir]",
                      "value": []
                  }
              }
          },
          "file": {
              "default": {
                  "default": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -asic_file 'yosys presyn ~/presyn.tcl'",
                          "api: chip.set('asic','file','yosys','presyn','~/presyn.tcl')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "List of named files specified on a per tool basis.\nThe parameter should only be used for specifying files that are\nnot directly supported by the ASIC schema.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "ASIC: special file",
                      "signature": [],
                      "switch": "-asic_file 'tool key<file>'",
                      "type": "[file]",
                      "value": []
                  }
              }
          },
          "footprint": {
              "default": {
                  "alias": {
                      "defvalue": [],
                      "example": [
                          "cli: -asic_footprint_alias '12track FreeCell'",
                          "api: chip.set('asic','footprint','12track','alias','FreeCell')"
                      ],
                      "help": "Alias for the footprint key that is sometimes needed when the footprint can\nbe referenced by multiple names. The key is the 'official' footprint.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "ASIC: Footprint name aliases",
                      "signature": [],
                      "switch": "-asic_footprint_alias 'key <str>'",
                      "type": "[str]",
                      "value": []
                  },
                  "size": {
                      "defvalue": null,
                      "example": [
                          "cli: -asic_footprint_size 'core (1.0,1.0)'",
                          "api: chip.set('asic','footprint','core','size',(1.0,1.0))"
                      ],
                      "help": "Size of the footprint described as a (width, height) tuple in\nmicrons.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "ASIC: Footprint size",
                      "signature": null,
                      "switch": "-asic_footprint_size 'key <str>'",
                      "type": "(float,float)",
                      "value": null
                  },
                  "symmetry": {
                      "defvalue": null,
                      "example": [
                          "cli: -asic_footprint_symmetry 'core X Y'",
                          "api: chip.set('asic','footprint','core','symmetry','X Y')"
                      ],
                      "help": "Footprint symmetry based on LEF standard definition. 'X' implies\nsymmetric about the x axis, 'Y' implies symmetry about the y axis, and\n'X Y' implies symmetry about the x and y axis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "ASIC: Footprint symmetry",
                      "signature": null,
                      "switch": "-asic_footprint_symmetry 'key <str>'",
                      "type": "str",
                      "value": null
                  }
              }
          },
          "hpinlayer": {
              "defvalue": null,
              "example": [
                  "cli: -asic_hpinlayer m4",
                  "api: chip.set('asic', 'hpinlayer', 'm4')"
              ],
              "help": "Metal layer to use for automated horizontal pin placement\nduring APR.  The metal layers can be specified as technology\nagnostic SC layers starting with m1 or as hard PDK specific\nlayer names.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: vertical pin layer",
              "signature": null,
              "switch": "-asic_hpinlayer <str>",
              "type": "str",
              "value": null
          },
          "libarch": {
              "defvalue": null,
              "example": [
                  "cli: -asic_libarch '12track'",
                  "api: chip.set('asic','libarch','12track')"
              ],
              "help": "The library architecture (e.g. library height) used to build the\ndesign. For example a PDK with support for 9 and 12 track libraries\nmight have 'libarchs' called 9t and 12t.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: library architecture",
              "signature": null,
              "switch": "-asic_libarch '<str>'",
              "type": "str",
              "value": null
          },
          "logiclib": {
              "defvalue": [],
              "example": [
                  "cli: -asic_logiclib nangate45",
                  "api: chip.set('asic', 'logiclib','nangate45')"
              ],
              "help": "List of all selected logic libraries libraries\nto use for optimization for a given library architecture\n(9T, 11T, etc).",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: logic libraries",
              "signature": [],
              "switch": "-asic_logiclib <str>",
              "type": "[str]",
              "value": []
          },
          "macrolib": {
              "defvalue": [],
              "example": [
                  "cli: -asic_macrolib sram64x1024",
                  "api: chip.set('asic', 'macrolib','sram64x1024')"
              ],
              "help": "List of macro libraries to be linked in during synthesis and place\nand route. Macro libraries are used for resolving instances but are\nnot used as targets for logic synthesis.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: macro libraries",
              "signature": [],
              "switch": "-asic_macrolib <str>",
              "type": "[str]",
              "value": []
          },
          "maxcap": {
              "defvalue": null,
              "example": [
                  "cli: -asic_maxcap '0.25e-12'",
                  "api: chip.set('asic', 'maxcap', '0.25e-12')"
              ],
              "help": "Maximum allowed capacitance per net.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: maximum net capacitance",
              "signature": null,
              "switch": "-asic_maxcap <float>",
              "type": "float",
              "unit": "F",
              "value": null
          },
          "maxfanout": {
              "defvalue": null,
              "example": [
                  "cli: -asic_maxfanout 64",
                  "api: chip.set('asic', 'maxfanout', '64')"
              ],
              "help": " Maximum driver fanout allowed during automated place and route.\nThe parameter directs the APR tool to break up any net with fanout\nlarger than maxfanout into sub nets and buffer.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: maximum fanout",
              "signature": null,
              "switch": "-asic_maxfanout <int>",
              "type": "int",
              "value": null
          },
          "maxlayer": {
              "defvalue": null,
              "example": [
                  "cli: -asic_maxlayer m2",
                  "api: chip.set('asic', 'maxlayer', 'm2')"
              ],
              "help": "Maximum SC metal layer name to be used for automated place and route .\nAlternatively the layer can be a string that matches a layer hard coded\nin the pdk_aprtech file. Designers wishing to use the same setup across\nmultiple process nodes should use the integer approach. For processes\nwith ambiguous starting routing layers, exact strings should be used.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: maximum routing layer",
              "signature": null,
              "switch": "-asic_maxlayer <str>",
              "type": "str",
              "value": null
          },
          "maxlength": {
              "defvalue": null,
              "example": [
                  "cli: -asic_maxlength 1000",
                  "api: chip.set('asic', 'maxlength', '1000')"
              ],
              "help": "Maximum total wire length allowed in design during APR. Any\nnet that is longer than maxlength is broken up into segments by\nthe tool.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: maximum wire length",
              "signature": null,
              "switch": "-asic_maxlength <float>",
              "type": "float",
              "unit": "um",
              "value": null
          },
          "maxslew": {
              "defvalue": null,
              "example": [
                  "cli: -asic_maxslew '0.25e-9'",
                  "api: chip.set('asic', 'maxslew', '0.25e-9')"
              ],
              "help": "Maximum allowed transition time per net.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: maximum slew",
              "signature": null,
              "switch": "-asic_maxslew <float>",
              "type": "float",
              "unit": "s",
              "value": null
          },
          "minlayer": {
              "defvalue": null,
              "example": [
                  "cli: -asic_minlayer m2",
                  "api: chip.set('asic', 'minlayer', 'm2')"
              ],
              "help": "Minimum SC metal layer name to be used for automated place and route .\nAlternatively the layer can be a string that matches a layer hard coded\nin the pdk_aprtech file. Designers wishing to use the same setup across\nmultiple process nodes should use the integer approach. For processes\nwith ambiguous starting routing layers, exact strings should be used.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: minimum routing layer",
              "signature": null,
              "switch": "-asic_minlayer <str>",
              "type": "str",
              "value": null
          },
          "ndr": {
              "default": {
                  "defvalue": null,
                  "example": [
                      "cli: -asic_ndr 'clk (0.2,0.2)'",
                      "api: chip.set('asic','ndr','clk', (0.2,0.2))"
                  ],
                  "help": "Definitions of non-default routing rule specified on a per\nnet basis. Constraints are entered as a (width,space) tuples\nspecified in microns.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: non-default routing rule",
                  "signature": null,
                  "switch": "-asic_ndr 'netname <(float,float)>",
                  "type": "(float,float)",
                  "value": null
              }
          },
          "pdk": {
              "defvalue": null,
              "example": [
                  "cli: -asic_pdk freepdk45",
                  "api: chip.set('asic','pdk','freepdk45')"
              ],
              "help": "Target ASIC PDK to use in the design.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC PDK target",
              "signature": null,
              "switch": "-asic_pdk <str>",
              "type": "str",
              "value": null
          },
          "pgmetal": {
              "defvalue": null,
              "example": [
                  "cli: -asic_pgmetal m1",
                  "api: chip.set('asic','pgmetal','m1')"
              ],
              "help": "Top metal layer used for power and ground routing within the\nlibrary. The parameter can be used to guide cell power grid\nhookup by APR tools.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: powergrid layer",
              "signature": null,
              "switch": "-asic_pgmetal '<str>'",
              "type": "str",
              "value": null
          },
          "rclayer": {
              "default": {
                  "defvalue": null,
                  "example": [
                      "cli: -asic_rclayer 'clk m3'",
                      "api: chip.set('asic', 'rclayer', 'clk', 'm3')"
                  ],
                  "help": "Technology agnostic metal layer to be used for parasitic\nextraction estimation during APR for the wire type specified\nCurrent the supported wire types are: clk, data. The metal\nlayers can be specified as technology agnostic SC layers\nstarting with m1 or as hard PDK specific layer names.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "ASIC: parasitics layer",
                  "signature": null,
                  "switch": "-asic_rclayer 'sigtype <str>'",
                  "type": "str",
                  "value": null
              }
          },
          "stackup": {
              "defvalue": null,
              "example": [
                  "cli: -asic_stackup 2MA4MB2MC",
                  "api: chip.set('asic','stackup','2MA4MB2MC')"
              ],
              "help": "Target ASIC stackup to use in the design. The stackup is required\nparameter for PDKs with multiple metal stackups.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC Stackup target",
              "signature": null,
              "switch": "-asic_stackup <str>",
              "type": "str",
              "value": null
          },
          "var": {
              "default": {
                  "default": {
                      "defvalue": [],
                      "example": [
                          "cli: -asic_variable 'xyce modeltype bsim4'",
                          "api: chip.set('asic','var','xyce','modeltype','bsim4')"
                      ],
                      "help": "List of key/value strings specified on a per basis. The parameter\nshould only be used for specifying variables that are\nnot directly supported by the SiliconCompiler PDK schema.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "ASIC: special variable",
                      "signature": [],
                      "switch": "-asic_variable 'tool key <str>'",
                      "type": "[str]",
                      "value": []
                  }
              }
          },
          "vpinlayer": {
              "defvalue": null,
              "example": [
                  "cli: -asic_vpinlayer m3",
                  "api: chip.set('asic', 'vpinlayer', 'm3')"
              ],
              "help": "Metal layer to use for automated vertical pin placement\nduring APR.  The metal layers can be specified as technology\nagnostic SC layers starting with m1 or as hard PDK specific\nlayer names.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "ASIC: vertical pin layer",
              "signature": null,
              "switch": "-asic_vpinlayer <str>",
              "type": "str",
              "value": null
          }
      },
      "checklist": {
          "default": {
              "default": {
                  "criteria": {
                      "defvalue": [],
                      "example": [
                          "cli: -checklist_criteria 'ISO D000 errors==0'",
                          "api: chip.set('checklist','ISO','D000','criteria','errors==0')"
                      ],
                      "help": "Simple list of signoff criteria for checklist item which\nmust all be met for signoff. Each signoff criteria consists of\na metric, a relational operator, and a value in the form.\n'metric op value'.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item criteria",
                      "signature": [],
                      "switch": "-checklist_criteria 'standard item <float>'",
                      "type": "[str]",
                      "value": []
                  },
                  "dataformat": {
                      "defvalue": null,
                      "example": [
                          "cli: -checklist_dataformat 'ISO D000 dataformat README'",
                          "api: chip.set('checklist','ISO','D000','dataformat','README')"
                      ],
                      "help": "Free text description of the type of data files acceptable as\nchecklist signoff validation.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item data format",
                      "signature": null,
                      "switch": "-checklist_dataformat 'standard item <float>'",
                      "type": "str",
                      "value": null
                  },
                  "description": {
                      "defvalue": null,
                      "example": [
                          "cli: -checklist_description 'ISO D000 A-DESCRIPTION'",
                          "api: chip.set('checklist','ISO','D000','description','A-DESCRIPTION')"
                      ],
                      "help": "A short one line description of the checklist item.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item description",
                      "signature": null,
                      "switch": "-checklist_description 'standard item <str>",
                      "type": "str",
                      "value": null
                  },
                  "ok": {
                      "defvalue": "false",
                      "example": [
                          "cli: -checklist_ok 'ISO D000 true'",
                          "api: chip.set('checklist','ISO','D000','ok', True)"
                      ],
                      "help": "Boolean check mark for the checklist item. A value of\nTrue indicates a human has inspected the all item dictionary\nparameters check out.",
                      "lock": "false",
                      "notes": null,
                      "require": "all",
                      "scope": "global",
                      "shorthelp": "Checklist: item ok",
                      "signature": null,
                      "switch": "-checklist_ok 'standard item <str>'",
                      "type": "bool",
                      "value": "false"
                  },
                  "rationale": {
                      "defvalue": [],
                      "example": [
                          "cli: -checklist_rational 'ISO D000 reliability'",
                          "api: chip.set('checklist','ISO','D000','rationale','reliability')"
                      ],
                      "help": "Rationale for the the checklist item. Rationale should be a\nunique alphanumeric code used by the standard or a short one line\nor single word description.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item rational",
                      "signature": [],
                      "switch": "-checklist_rationale 'standard item <str>",
                      "type": "[str]",
                      "value": []
                  },
                  "report": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -checklist_report 'ISO D000 my.rpt'",
                          "api: chip.set('checklist','ISO','D000','report','my.rpt')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to report(s) of specified type documenting the successful\nvalidation of the checklist item.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item report",
                      "signature": [],
                      "switch": "-checklist_report 'standard item <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "requirement": {
                      "defvalue": null,
                      "example": [
                          "cli: -checklist_requirement 'ISO D000 DOCSTRING'",
                          "api: chip.set('checklist','ISO','D000','requirement','DOCSTRING')"
                      ],
                      "help": "A complete requirement description of the checklist item\nentered as a multi-line string.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item requirement",
                      "signature": null,
                      "switch": "-checklist_requirement 'standard item <str>",
                      "type": "str",
                      "value": null
                  },
                  "task": {
                      "defvalue": [],
                      "example": [
                          "cli: -checklist_task 'ISO D000 (job0,place,0)'",
                          "api: chip.set('checklist','ISO','D000','task',('job0','place','0'))"
                      ],
                      "help": "Flowgraph job and task used to verify the checklist item.\nThe parameter should be left empty for manual and for tool\nflows that bypass the SC infrastructure.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Checklist: item task",
                      "signature": [],
                      "switch": "-checklist_task 'standard item <(str, str, str)>'",
                      "type": "[(str,str,str)]",
                      "value": []
                  },
                  "waiver": {
                      "default": {
                          "author": [],
                          "copy": "false",
                          "date": [],
                          "defvalue": [],
                          "example": [
                              "cli: -checklist_waiver 'ISO D000 bold my.txt'",
                              "api: chip.set('checklist','ISO','D000','waiver','hold', 'my.txt')"
                          ],
                          "filehash": [],
                          "hashalgo": "sha256",
                          "help": "Filepath to report(s) documenting waivers for the checklist\nitem specified on a per metric basis.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "global",
                          "shorthelp": "Checklist: item metric waivers",
                          "signature": [],
                          "switch": "-checklist_waiver 'standard item metric <file>'",
                          "type": "[file]",
                          "value": []
                      }
                  }
              }
          }
      },
      "constraint": {
          "default": {
              "check": {
                  "defvalue": [],
                  "example": [
                      "cli: -constraint_check 'worst check setup'",
                      "api: chip.add('constraint','worst','check','setup')"
                  ],
                  "help": "List of checks for to perform for the scenario. The checks must\nalign with the capabilities of the EDA tools and flow being used.\nChecks generally include objectives like meeting setup and hold goals\nand minimize power. Standard check names include setup, hold, power,\nnoise, reliability.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint checks to perform",
                  "signature": [],
                  "switch": "-constraint_check 'scenario <str>'",
                  "type": "[str]",
                  "value": []
              },
              "file": {
                  "author": [],
                  "copy": "true",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -constraint_file 'worst hello.sdc'",
                      "api: chip.set('constraint','worst','file', 'hello.sdc')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "List of timing constraint files to use for the scenario. The\nvalues are combined with any constraints specified by the design\n'constraint' parameter. If no constraints are found, a default\nconstraint file is used based on the clock definitions.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint files",
                  "signature": [],
                  "switch": "-constraint_file 'scenario <file>'",
                  "type": "[file]",
                  "value": []
              },
              "libcorner": {
                  "defvalue": null,
                  "example": [
                      "cli: -constraint_libcorner 'worst ttt'",
                      "api: chip.set('constraint', 'worst', 'libcorner', 'ttt')"
                  ],
                  "help": "Library corner applied to the scenario to scale\nlibrary timing models based on the libcorner value for models\nthat support it. The parameter is ignored for libraries that\nhave one hard coded model per libcorner.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint library corner",
                  "signature": null,
                  "switch": "-constraint_libcorner 'scenario <str>'",
                  "type": "str",
                  "value": null
              },
              "mode": {
                  "defvalue": null,
                  "example": [
                      "cli: -constraint_mode 'worst test'",
                      "api: chip.set('constraint',  'worst','mode', 'test')"
                  ],
                  "help": "Operating mode for the scenario. Operating mode strings\ncan be values such as test, functional, standby.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint operating mode",
                  "signature": null,
                  "switch": "-constraint_mode 'scenario <str>'",
                  "type": "str",
                  "value": null
              },
              "opcond": {
                  "defvalue": null,
                  "example": [
                      "cli: -constraint_opcond 'worst typical_1.0'",
                      "api: chip.set('constraint', 'worst', 'opcond',  'typical_1.0')"
                  ],
                  "help": "Operating condition applied to the scenario. The value\ncan be used to access specific conditions within the library\ntiming models from the 'logiclib' timing models.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint operating condition",
                  "signature": null,
                  "switch": "-constraint_opcond 'scenario <str>'",
                  "type": "str",
                  "value": null
              },
              "pexcorner": {
                  "defvalue": null,
                  "example": [
                      "cli: -constraint_pexcorner 'worst max'",
                      "api: chip.set('constraint', 'worst', 'pexcorner', 'max')"
                  ],
                  "help": "Parasitic corner applied to the scenario. The\n'pexcorner' string must match a corner found in the pdk\npexmodel setup.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint pex corner",
                  "signature": null,
                  "switch": "-constraint_pexcorner 'scenario <str>'",
                  "type": "str",
                  "value": null
              },
              "temperature": {
                  "defvalue": null,
                  "example": [
                      "cli: -constraint_temperature 'worst 125'",
                      "api: chip.set('constraint', 'worst', 'temperature','125')"
                  ],
                  "help": "Chip temperature applied to the scenario specified in degrees C.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint temperature",
                  "signature": null,
                  "switch": "-constraint_temperature 'scenario <float>'",
                  "type": "float",
                  "value": null
              },
              "voltage": {
                  "defvalue": null,
                  "example": [
                      "cli: -constraint_voltage 'worst 0.9'",
                      "api: chip.set('constraint', 'worst','voltage', '0.9')"
                  ],
                  "help": "Operating voltage applied to the scenario.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Constraint voltage level",
                  "signature": null,
                  "switch": "-constraint_voltage 'scenario <float>'",
                  "type": "float",
                  "unit": "V",
                  "value": null
              }
          }
      },
      "datasheet": {
          "default": {
              "feature": {
                  "default": {
                      "defvalue": null,
                      "example": [
                          "cli: -datasheet_feature 'mydevice ram 64e6'",
                          "api: chip.set('datasheet','mydevice','feature','ram', 1e9)"
                      ],
                      "help": "Quantity of a specified feature. The 'unit'\nfield should be used to specify the units used when unclear.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Datasheet: device feature specification",
                      "signature": null,
                      "switch": "-datasheet_feature 'design name <float>'",
                      "type": "float",
                      "value": null
                  }
              },
              "limits": {
                  "junctiontemp": {
                      "defvalue": null,
                      "example": [
                          "cli: -datasheet_junctiontemp 'mydevice (-40,125)'",
                          "api: chip.set('datasheet','mydevice','limits','junctiontemp',(-40,125))"
                      ],
                      "help": "Device absolute junction temperature limits not to be exceeded.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Datasheet: absolute junction temperature limits",
                      "signature": null,
                      "switch": "-datasheet_junctiontemp 'design <(float,float)>'",
                      "type": "(float,float)",
                      "value": null
                  },
                  "storagetemp": {
                      "defvalue": null,
                      "example": [
                          "cli: -datasheet_storagetemp 'mydevice (-40,125)'",
                          "api: chip.set('datasheet','mydevice','limits','storagetemp',(-40,125))"
                      ],
                      "help": "Device absolute storage temperature limits not to be exceeded.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Datasheet: absolute storage temperature limits",
                      "signature": null,
                      "switch": "-datasheet_storagetemp 'design <(float,float)>'",
                      "type": "(float,float)",
                      "value": null
                  },
                  "voltage": {
                      "default": {
                          "defvalue": null,
                          "example": [
                              "cli: -datasheet_limits_voltage 'mydevice vdd (-0.4,1.1)'",
                              "api: chip.set('datasheet','mydevice','limits','voltage','vdd', (-0.4,1.1))"
                          ],
                          "help": "Device absolute minimum/maximum voltage not to be\nexceeded, specified on a per pin basis.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Datasheet: absolute voltage limits",
                          "signature": null,
                          "switch": "-datasheet_limits_voltage 'design pin <(float,float)>'",
                          "type": "(float,float)",
                          "value": null
                      }
                  }
              },
              "pin": {
                  "default": {
                      "capacitance": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_capacitance 'mydevice sclk global (1e-12, 1.2e-12, 1.5e-12)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','capacitance','global',(1e-12, 1.2e-12, 1.5e-12)"
                              ],
                              "help": "Pin capacitance. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: capacitance",
                              "signature": null,
                              "switch": "-datasheet_pin_capacitance 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "F",
                              "value": null
                          }
                      },
                      "clk": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_clk 'mydevice ina global clka'",
                                  "api: chip.set('datasheet','mydevice','pin','ina','clk','global','clka')"
                              ],
                              "help": "Pin related clock specified on a per mode basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin related clock",
                              "signature": null,
                              "switch": "-datasheet_pin_clk 'design name mode <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "complement": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_complement 'mydevice ina global inb'",
                                  "api: chip.set('datasheet','mydevice','pin','ina','complement','global','inb')"
                              ],
                              "help": "Pin complement specified on a per mode basis for differential\nsignals.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin complement",
                              "signature": null,
                              "switch": "-datasheet_pin_complement 'design name mode <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "dir": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_dir 'mydevice clk global input'",
                                  "api: chip.set('datasheet','mydevice','pin','clk','dir','global','input')"
                              ],
                              "help": "Pin direction specified on a per mode basis. Acceptable pin\ndirections include: input, output, inout.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin direction",
                              "signature": null,
                              "switch": "-datasheet_pin_dir 'design name mode <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "dutycycle": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_dutycycle 'mydevice sclk global (45, 50, 55)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','dutycycle','global',(45, 50, 55)"
                              ],
                              "help": "Pin duty cycle. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: duty cycle",
                              "signature": null,
                              "switch": "-datasheet_pin_dutycycle 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "%",
                              "value": null
                          }
                      },
                      "ground": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_ground 'mydevice ina ground vss'",
                                  "api: chip.set('datasheet','mydevice','pin','ina','ground','global','vss')"
                              ],
                              "help": "Pin related ground rail specified on a per mode basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin related ground",
                              "signature": null,
                              "switch": "-datasheet_pin_ground 'design name mode <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "idrive": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_idrive 'mydevice sclk global (0.01, 0.012, 0.015)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','idrive','global',(0.01, 0.012, 0.015)"
                              ],
                              "help": "Pin drive current. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: drive current",
                              "signature": null,
                              "switch": "-datasheet_pin_idrive 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "A",
                              "value": null
                          }
                      },
                      "iinject": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_iinject 'mydevice sclk global (0.001, 0.0012, 0.0015)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','iinject','global',(0.001, 0.0012, 0.0015)"
                              ],
                              "help": "Pin injection current. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: injection current",
                              "signature": null,
                              "switch": "-datasheet_pin_iinject 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "A",
                              "value": null
                          }
                      },
                      "ileakage": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_ileakage 'mydevice sclk global (1e-06, 1.2e-06, 1.5e-06)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','ileakage','global',(1e-06, 1.2e-06, 1.5e-06)"
                              ],
                              "help": "Pin leakage current. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: leakage current",
                              "signature": null,
                              "switch": "-datasheet_pin_ileakage 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "A",
                              "value": null
                          }
                      },
                      "map": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_map 'mydevice in0 bga512 B4'",
                                  "api: chip.set('datasheet','mydevice','pin','in0','map','bga512','B4')"
                              ],
                              "help": "Signal to package pin mapping specified on a per package basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: package pin map",
                              "signature": null,
                              "switch": "-datasheet_pin_map 'design name package <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "rdiff": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_rdiff 'mydevice sclk global (45, 50, 55)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','rdiff','global',(45, 50, 55)"
                              ],
                              "help": "Pin differential pair resistance. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: differential pair resistance",
                              "signature": null,
                              "switch": "-datasheet_pin_rdiff 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "ohm",
                              "value": null
                          }
                      },
                      "resetvalue": {
                          "default": {
                              "defvalue": [],
                              "example": [
                                  "cli: -datasheet_pin_resetvalue 'mydevice clk global weak1'",
                                  "api: chip.set('datasheet','mydevice','pin','clk','resetvalue','global','weak1')"
                              ],
                              "help": "Pin reset value specified on a per mode basis. Legal reset\nvalues include weak1, weak0, strong0, strong1, highz.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin reset value",
                              "signature": [],
                              "switch": "-datasheet_pin_resetvalue 'design name mode <str>'",
                              "type": "[str]",
                              "value": []
                          }
                      },
                      "rpulldown": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_rpulldown 'mydevice sclk global (1000, 1200, 3000)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','rpulldown','global',(1000, 1200, 3000)"
                              ],
                              "help": "Pin pulldown resistance. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pulldown resistance",
                              "signature": null,
                              "switch": "-datasheet_pin_rpulldown 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "ohm",
                              "value": null
                          }
                      },
                      "rpullup": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_rpullup 'mydevice sclk global (1000, 1200, 3000)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','rpullup','global',(1000, 1200, 3000)"
                              ],
                              "help": "Pin pullup resistance. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pullup resistance",
                              "signature": null,
                              "switch": "-datasheet_pin_rpullup 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "ohm",
                              "value": null
                          }
                      },
                      "standard": {
                          "default": {
                              "defvalue": [],
                              "example": [
                                  "cli: -datasheet_pin_standard 'mydevice ba0 global ddr4'",
                                  "api: chip.set('datasheet','mydevice','pin','ina','standard','global','ddr4')"
                              ],
                              "help": "Pin communication standard specified on a per mode basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin standard",
                              "signature": [],
                              "switch": "-datasheet_pin_standard 'design name mode <str>'",
                              "type": "[str]",
                              "value": []
                          }
                      },
                      "supply": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_supply 'mydevice ina global vdd'",
                                  "api: chip.set('datasheet','mydevice','pin','ina','supply','global','vdd')"
                              ],
                              "help": "Pin related power supply specified on a per mode basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin related power supply",
                              "signature": null,
                              "switch": "-datasheet_pin_supply 'design name mode <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "tfall": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_tfall 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','tfall','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin fall transition. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: fall transition",
                              "signature": null,
                              "switch": "-datasheet_pin_tfall 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "thold": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_thold 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','thold','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin hold time. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: hold time",
                              "signature": null,
                              "switch": "-datasheet_pin_thold 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "tjitter": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_tjitter 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','tjitter','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin rms jitter. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: rms jitter",
                              "signature": null,
                              "switch": "-datasheet_pin_tjitter 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "tperiod": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_tperiod 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','tperiod','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin minimum period. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: minimum period",
                              "signature": null,
                              "switch": "-datasheet_pin_tperiod 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "tpulse": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_tpulse 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','tpulse','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin pulse width. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pulse width",
                              "signature": null,
                              "switch": "-datasheet_pin_tpulse 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "trise": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_trise 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','trise','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin rise transition. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: rise transition",
                              "signature": null,
                              "switch": "-datasheet_pin_trise 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "tsetup": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_tsetup 'mydevice sclk global (1e-09, 2e-09, 4e-09)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','tsetup','global',(1e-09, 2e-09, 4e-09)"
                              ],
                              "help": "Pin setup time. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: setup time",
                              "signature": null,
                              "switch": "-datasheet_pin_tsetup 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "s",
                              "value": null
                          }
                      },
                      "type": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_type 'mydevice vdd type power'",
                                  "api: chip.set('datasheet','mydevice','pin','vdd','type','global','power')"
                              ],
                              "help": "Pin type specified on a per mode basis. Acceptable pin types\ninclude: digital, analog, clk, power, ground",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: pin type",
                              "signature": null,
                              "switch": "-datasheet_pin_type 'design name mode <str>'",
                              "type": "str",
                              "value": null
                          }
                      },
                      "vcdm": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vcdm 'mydevice sclk global (100, 300, 500)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vcdm','global',(100, 300, 500)"
                              ],
                              "help": "Pin human body model ESD tolerance. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: human body model ESD tolerance",
                              "signature": null,
                              "switch": "-datasheet_pin_vcdm 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vcm": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vcm 'mydevice sclk global (0.3, 1.2, 1.6)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vcm','global',(0.3, 1.2, 1.6)"
                              ],
                              "help": "Pin common mode voltage. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: common mode voltage",
                              "signature": null,
                              "switch": "-datasheet_pin_vcm 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vdiff": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vdiff 'mydevice sclk global (0.2, 0.3, 0.9)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vdiff','global',(0.2, 0.3, 0.9)"
                              ],
                              "help": "Pin differential voltage. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: differential voltage",
                              "signature": null,
                              "switch": "-datasheet_pin_vdiff 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vhbm": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vhbm 'mydevice sclk global (100, 300, 500)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vhbm','global',(100, 300, 500)"
                              ],
                              "help": "Pin machine model ESD tolerance. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: machine model ESD tolerance",
                              "signature": null,
                              "switch": "-datasheet_pin_vhbm 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vih": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vih 'mydevice sclk global (1.4, 1.8, 2.2)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vih','global',(1.4, 1.8, 2.2)"
                              ],
                              "help": "Pin high input voltage level. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: high input voltage level",
                              "signature": null,
                              "switch": "-datasheet_pin_vih 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vil": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vil 'mydevice sclk global (-0.2, 0, 1.0)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vil','global',(-0.2, 0, 1.0)"
                              ],
                              "help": "Pin low input voltage level. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: low input voltage level",
                              "signature": null,
                              "switch": "-datasheet_pin_vil 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vnoise": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vnoise 'mydevice sclk global (0, 0.01, 0.1)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vnoise','global',(0, 0.01, 0.1)"
                              ],
                              "help": "Pin random voltage noise. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: random voltage noise",
                              "signature": null,
                              "switch": "-datasheet_pin_vnoise 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "voh": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_voh 'mydevice sclk global (4.6, 4.8, 5.2)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','voh','global',(4.6, 4.8, 5.2)"
                              ],
                              "help": "Pin high output voltage level. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: high output voltage level",
                              "signature": null,
                              "switch": "-datasheet_pin_voh 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      },
                      "vol": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -datasheet_pin_vol 'mydevice sclk global (-0.2, 0, 0.2)'",
                                  "api: chip.set('datasheet','mydevice','pin','sclk','vol','global',(-0.2, 0, 0.2)"
                              ],
                              "help": "Pin low output voltage level. Values are tuples of (min, typical, max).",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Datasheet: low output voltage level",
                              "signature": null,
                              "switch": "-datasheet_pin_vol 'design pin mode <(float,float,float)>'",
                              "type": "(float,float,float)",
                              "unit": "V",
                              "value": null
                          }
                      }
                  }
              }
          }
      },
      "design": {
          "defvalue": null,
          "example": [
              "cli: -design hello_world",
              "api: chip.set('design', 'hello_world')"
          ],
          "help": "Name of the top level module or library. Required for all\nchip objects.",
          "lock": "false",
          "notes": null,
          "require": "all",
          "scope": "global",
          "shorthelp": "Design top module name",
          "signature": null,
          "switch": "-design <str>",
          "type": "str",
          "value": null
      },
      "flowgraph": {
          "default": {
              "default": {
                  "default": {
                      "args": {
                          "defvalue": [],
                          "example": [
                              "cli: -flowgraph_args 'asicflow cts 0 0'",
                              "api:  chip.add('flowgraph','asicflow','cts','0','args','0')"
                          ],
                          "help": "User specified flowgraph string arguments specified on a per\nstep and per index basis.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Flowgraph: setup arguments",
                          "signature": [],
                          "switch": "-flowgraph_args 'flow step index <str>'",
                          "type": "[str]",
                          "value": []
                      },
                      "goal": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -flowgraph_goal 'asicflow cts 0 area_cells 1.0'",
                                  "api:  chip.set('flowgraph','asicflow','cts','0','goal','errors', 0)"
                              ],
                              "help": "Goals specified on a per step and per metric basis used to\ndetermine whether a certain task can be considered when merging\nmultiple tasks at a minimum or maximum node. A task is considered\nfailing if the absolute value of any of its metrics are larger than\nthe goal for that metric, if set.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Flowgraph: metric goals",
                              "signature": null,
                              "switch": "-flowgraph_goal 'flow step index metric <float>'",
                              "type": "float",
                              "value": null
                          }
                      },
                      "input": {
                          "defvalue": [],
                          "example": [
                              "cli: -flowgraph_input 'asicflow cts 0 (place,0)'",
                              "api:  chip.set('flowgraph','asicflow','cts','0','input',('place','0'))"
                          ],
                          "help": "A list of inputs for the current step and index, specified as a\n(step,index) tuple.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Flowgraph: step input",
                          "signature": [],
                          "switch": "-flowgraph_input 'flow step index <(str,str)>'",
                          "type": "[(str,str)]",
                          "value": []
                      },
                      "select": {
                          "defvalue": [],
                          "example": [
                              "cli: -flowgraph_select 'asicflow cts 0 (place,42)'",
                              "api:  chip.set('flowgraph','asicflow', 'cts','0','select',('place','42'))"
                          ],
                          "help": "List of selected inputs for the current step/index specified as\n(in_step,in_index) tuple.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Flowgraph: task select record",
                          "signature": [],
                          "switch": "-flowgraph_select 'flow step index <(str,str)>'",
                          "type": "[(str,str)]",
                          "value": []
                      },
                      "status": {
                          "defvalue": null,
                          "example": [
                              "cli: -flowgraph_status 'asicflow cts 10 success'",
                              "api:  chip.set('flowgraph','asicflow', 'cts','10','status', 'success')"
                          ],
                          "help": "Parameter that tracks the status of a task. Valid values are:\n\n* \"success\": task ran successfully\n* \"error\": task failed with an error\n\nAn empty value indicates the task has not yet been completed.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Flowgraph: task status",
                          "signature": null,
                          "switch": "-flowgraph_status 'flow step index <str>'",
                          "type": "str",
                          "value": null
                      },
                      "timeout": {
                          "defvalue": null,
                          "example": [
                              "cli: -flowgraph_timeout 'asicflow cts 0 3600'",
                              "api:  chip.set('flowgraph','asicflow','cts','0','timeout', 3600)"
                          ],
                          "help": "Timeout value in seconds specified on a per step and per index\nbasis. The flowgraph timeout value is compared against the\nwall time tracked by the SC runtime to determine if an\noperation should continue. Timeout values help in situations\nwhere 1.) an operation is stuck and may never finish. 2.) the\noperation progress has saturated and continued execution has\na negative return on investment.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Flowgraph: task timeout value",
                          "signature": null,
                          "switch": "-flowgraph_timeout 'flow step 0 <float>'",
                          "type": "float",
                          "unit": "s",
                          "value": null
                      },
                      "tool": {
                          "defvalue": null,
                          "example": [
                              "cli: -flowgraph_tool 'asicflow place 0 openroad'",
                              "api: chip.set('flowgraph','asicflow','place','0','tool','openroad')"
                          ],
                          "help": "Name of the tool name used for task execution. Builtin tool names\nassociated bound to core API functions include: minimum, maximum, join,\nverify, mux.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Flowgraph: tool selection",
                          "signature": null,
                          "switch": "-flowgraph_tool 'flow step <str>'",
                          "type": "str",
                          "value": null
                      },
                      "valid": {
                          "defvalue": "false",
                          "example": [
                              "cli: -flowgraph_valid 'asicflow cts 0 true'",
                              "api:  chip.set('flowgraph','asicflow','cts','0','valid',True)"
                          ],
                          "help": "Flowgraph valid bit specified on a per step and per index basis.\nThe parameter can be used to control flow execution. If the bit\nis cleared (0), then the step/index combination is invalid and\nshould not be run.",
                          "lock": "false",
                          "notes": null,
                          "require": "all",
                          "scope": "job",
                          "shorthelp": "Flowgraph: task valid bit",
                          "signature": null,
                          "switch": "-flowgraph_valid 'flow step index <str>'",
                          "type": "bool",
                          "value": "false"
                      },
                      "weight": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -flowgraph_weight 'asicflow cts 0 area_cells 1.0'",
                                  "api:  chip.set('flowgraph','asicflow','cts','0','weight','area_cells',1.0)"
                              ],
                              "help": "Weights specified on a per step and per metric basis used to give\neffective \"goodnes\" score for a step by calculating the sum all step\nreal metrics results by the corresponding per step weights.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Flowgraph: metric weights",
                              "signature": null,
                              "switch": "-flowgraph_weight 'flow step index metric <float>'",
                              "type": "float",
                              "value": null
                          }
                      }
                  }
              }
          }
      },
      "fpga": {
          "arch": {
              "author": [],
              "copy": "true",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -fpga_arch myfpga.xml",
                  "api:  chip.set('fpga', 'arch', 'myfpga.xml')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "Architecture definition file for FPGA place and route\ntool. For the VPR tool, the file is a required XML based description,\nallowing targeting a large number of virtual and commercial\narchitectures. For most commercial tools, the fpga part name provides\nenough information to enable compilation and the 'arch' parameter is\noptional.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "FPGA: architecture file",
              "signature": [],
              "switch": "-fpga_arch <file>",
              "type": "[file]",
              "value": []
          },
          "board": {
              "defvalue": null,
              "example": [
                  "cli: -fpga_board parallella",
                  "api:  chip.set('fpga', 'board', 'parallella')"
              ],
              "help": "Complete board name used as a device target by the FPGA compilation\ntool. The board name must be an exact string match to the partname\nhard coded within the FPGA eda tool. The parameter is optional and can\nbe used in place of a partname and pin constraints for some tools.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "FPGA: board name",
              "signature": null,
              "switch": "-fpga_board <str>",
              "type": "str",
              "value": null
          },
          "flash": {
              "defvalue": "false",
              "example": [
                  "cli: -fpga_flash",
                  "api:  chip.set('fpga', 'flash', True)"
              ],
              "help": "Specifies that the bitstream should be flashed in the board/device.\nThe default is to load the bitstream into volatile memory (SRAM).",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "FPGA: flash enable",
              "signature": null,
              "switch": "-fpga_flash <bool>",
              "type": "bool",
              "value": "false"
          },
          "partname": {
              "defvalue": null,
              "example": [
                  "cli: -fpga_partname fpga64k",
                  "api:  chip.set('fpga', 'partname', 'fpga64k')"
              ],
              "help": "Complete part name used as a device target by the FPGA compilation\ntool. The part name must be an exact string match to the partname\nhard coded within the FPGA eda tool.",
              "lock": "false",
              "notes": null,
              "require": "fpga",
              "scope": "job",
              "shorthelp": "FPGA: part name",
              "signature": null,
              "switch": "-fpga_partname <str>",
              "type": "str",
              "value": null
          },
          "program": {
              "defvalue": "false",
              "example": [
                  "cli: -fpga_program",
                  "api:  chip.set('fpga', 'program', True)"
              ],
              "help": "Specifies that the bitstream should be loaded into an FPGA.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "FPGA: program enable",
              "signature": null,
              "switch": "-fpga_program <bool>",
              "type": "bool",
              "value": "false"
          },
          "vendor": {
              "defvalue": null,
              "example": [
                  "cli: -fpga_vendor acme",
                  "api:  chip.set('fpga', 'vendor', 'acme')"
              ],
              "help": "Name of the FPGA vendor. The parameter is used to check part\nname and to select the eda tool flow in case 'edaflow' is\nunspecified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "FPGA: vendor name",
              "signature": null,
              "switch": "-fpga_vendor <str>",
              "type": "str",
              "value": null
          }
      },
      "history": {},
      "input": {
          "default": {
              "author": [],
              "copy": "true",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -input 'verilog hello_world.v'",
                  "api: chip.set(input,'verilog','hello_world.v')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "List of input files specifed by type. The filetype name must\nalign with the parameter names within the flow and tool setup\nscripts. Examples of acceptable file types include python, c,\nsystemc, verilog, vhdl, netlist, def, gds, gerber, saif, sdc,\nsaif, vcd, spef, sdf.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Input files",
              "signature": [],
              "switch": "-input 'filetype <file>'",
              "type": "[file]",
              "value": []
          }
      },
      "library": {},
      "metric": {
          "default": {
              "default": {
                  "averagepower": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_averagepower 'place 0 0.01'",
                          "api: chip.set('metric','place','0','averagepower', 0.01)"
                      ],
                      "help": "Metric tracking the average workload power of the design specified on a per step\nand index basis. Power metric depend heavily on the method\nbeing used for extraction: dynamic vs static, workload\nspecification (vcd vs saif), power models, process/voltage/temperature.\nThe power averagepower metric tries to capture the data that would\nusually be reflected inside a datasheet given the approprate\nfootnote conditions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: averagepower",
                      "signature": null,
                      "switch": "-metric_averagepower 'step index <float>'",
                      "type": "float",
                      "unit": "mw",
                      "value": null
                  },
                  "brams": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_brams 'place 0 100'",
                          "api: chip.set('metric','place','0','brams', 100)"
                      ],
                      "help": "Metric tracking the total FPGA BRAM tiles used by the design as reported\nby the implementation tool. There is no standardized definition\nfor this metric across vendors, so metric comparisons can\ngenerally only be done between runs on identical tools and\ndevice families.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: FPGA BRAM tiles",
                      "signature": null,
                      "switch": "-metric_brams 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "buffers": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_buffers 'place 0 100'",
                          "api: chip.set('metric','place','0','buffers', 50)"
                      ],
                      "help": "Metric tracking the total number of buffer and inverter instances in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: buffers",
                      "signature": null,
                      "switch": "-metric_buffers 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "cellarea": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_cellarea 'place 0 100.00'",
                          "api: chip.set('metric','place','0','cellarea', 100.00)"
                      ],
                      "help": "Metric tracking the total cell area (ignoring fillers) occupied by the design.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: cellarea",
                      "signature": null,
                      "switch": "-metric_cellarea 'step index <float>'",
                      "type": "float",
                      "unit": "um^2",
                      "value": null
                  },
                  "cells": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_cells 'place 0 100'",
                          "api: chip.set('metric','place','0','cells', 50)"
                      ],
                      "help": "Metric tracking the total number of cell instances in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: cells",
                      "signature": null,
                      "switch": "-metric_cells 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "coverage": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_coverage 'place 0 99.9'",
                          "api: chip.set('metric','place','0','coverage', 99.9)"
                      ],
                      "help": "Metric tracking the test coverage in the design expressed as a percentage\nwith 100 meaning full coverage. The meaning of the metric depends on the\ntask being executed. It can refer to code coverage, feature coverage,\nstuck at fault coverage.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: coverage",
                      "signature": null,
                      "switch": "-metric_coverage 'step index <float>'",
                      "type": "float",
                      "unit": "%",
                      "value": null
                  },
                  "dozepower": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_dozepower 'place 0 0.01'",
                          "api: chip.set('metric','place','0','dozepower', 0.01)"
                      ],
                      "help": "Metric tracking the power consumed while in low frequency operating mode of the design specified on a per step\nand index basis. Power metric depend heavily on the method\nbeing used for extraction: dynamic vs static, workload\nspecification (vcd vs saif), power models, process/voltage/temperature.\nThe power dozepower metric tries to capture the data that would\nusually be reflected inside a datasheet given the approprate\nfootnote conditions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: dozepower",
                      "signature": null,
                      "switch": "-metric_dozepower 'step index <float>'",
                      "type": "float",
                      "unit": "mw",
                      "value": null
                  },
                  "drvs": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_drvs 'dfm 0 0'",
                          "api: chip.set('metric','dfm','0','drvs', 0)"
                      ],
                      "help": "Metric tracking the total number of design rule violations on a\nper step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: total drvs",
                      "signature": null,
                      "switch": "-metric_drvs 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "dsps": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_dsps 'place 0 100'",
                          "api: chip.set('metric','place','0','dsps', 100)"
                      ],
                      "help": "Metric tracking the total FPGA DSP slices used by the design as reported\nby the implementation tool. There is no standardized definition\nfor this metric across vendors, so metric comparisons can\ngenerally only be done between runs on identical tools and\ndevice families.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: FPGA DSP slices",
                      "signature": null,
                      "switch": "-metric_dsps 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "errors": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_errors 'dfm 0 0'",
                          "api: chip.set('metric','dfm','0','errors', 0)"
                      ],
                      "help": "Metric tracking the total number of errors on a\nper step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: total errors",
                      "signature": null,
                      "switch": "-metric_errors 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "exetime": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_exetime 'dfm 0 10.0'",
                          "api: chip.set('metric','dfm','0','exetime', 10.0)"
                      ],
                      "help": "Metric tracking time spent by the eda executable 'exe' on a\nper step and index basis. It does not include the siliconcompiler\nruntime overhead or time waitig for I/O operations and\ninter-processor communication to complete.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: exetime",
                      "signature": null,
                      "switch": "-metric_exetime 'step index <float>'",
                      "type": "float",
                      "unit": "s",
                      "value": null
                  },
                  "holdpaths": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_holdpaths 'place 0 10'",
                          "api: chip.set('metric','place','0','holdpaths', 10)"
                      ],
                      "help": "Metric tracking the total number of timing paths violating hold\nconstraints.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: holdpaths",
                      "signature": null,
                      "switch": "-metric_holdpaths 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "holdslack": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_holdslack 'place 0 0.01'",
                          "api: chip.set('metric','place','0','holdslack', 0.01)"
                      ],
                      "help": "Metric tracking the worst hold slack (positive or negative) on a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: holdslack",
                      "signature": null,
                      "switch": "-metric_holdslack 'step index <float>'",
                      "type": "float",
                      "unit": "ns",
                      "value": null
                  },
                  "holdtns": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_holdtns 'place 0 0.01'",
                          "api: chip.set('metric','place','0','holdtns', 0.01)"
                      ],
                      "help": "Metric tracking the total negative hold slack (TNS) on a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: holdtns",
                      "signature": null,
                      "switch": "-metric_holdtns 'step index <float>'",
                      "type": "float",
                      "unit": "ns",
                      "value": null
                  },
                  "holdwns": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_holdwns 'place 0 0.01'",
                          "api: chip.set('metric','place','0','holdwns', 0.01)"
                      ],
                      "help": "Metric tracking the worst negative hold slack (positive values truncated to zero) on a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: holdwns",
                      "signature": null,
                      "switch": "-metric_holdwns 'step index <float>'",
                      "type": "float",
                      "unit": "ns",
                      "value": null
                  },
                  "idlepower": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_idlepower 'place 0 0.01'",
                          "api: chip.set('metric','place','0','idlepower', 0.01)"
                      ],
                      "help": "Metric tracking the power while not performing useful work of the design specified on a per step\nand index basis. Power metric depend heavily on the method\nbeing used for extraction: dynamic vs static, workload\nspecification (vcd vs saif), power models, process/voltage/temperature.\nThe power idlepower metric tries to capture the data that would\nusually be reflected inside a datasheet given the approprate\nfootnote conditions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: idlepower",
                      "signature": null,
                      "switch": "-metric_idlepower 'step index <float>'",
                      "type": "float",
                      "unit": "mw",
                      "value": null
                  },
                  "irdrop": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_irdrop 'place 0 0.05'",
                          "api: chip.set('metric','place','0','irdrop', 0.05)"
                      ],
                      "help": "Metric tracking the peak IR drop in the design based on extracted\npower and ground rail parasitics, library power models, and\nswitching activity. The switching activity calculated on a per\nnode basis is taken from one of three possible sources, in order\nof priority: VCD file, SAIF file, 'activityfactor' parameter.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: peak IR drop",
                      "signature": null,
                      "switch": "-metric_irdrop 'step index <float>'",
                      "type": "float",
                      "unit": "mv",
                      "value": null
                  },
                  "leakagepower": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_leakagepower 'place 0 0.01'",
                          "api: chip.set('metric','place','0','leakagepower', 0.01)"
                      ],
                      "help": "Metric tracking the leakage power with rails active but without any dynamic switching activity of the design specified on a per step\nand index basis. Power metric depend heavily on the method\nbeing used for extraction: dynamic vs static, workload\nspecification (vcd vs saif), power models, process/voltage/temperature.\nThe power leakagepower metric tries to capture the data that would\nusually be reflected inside a datasheet given the approprate\nfootnote conditions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: leakagepower",
                      "signature": null,
                      "switch": "-metric_leakagepower 'step index <float>'",
                      "type": "float",
                      "unit": "mw",
                      "value": null
                  },
                  "luts": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_luts 'place 0 100'",
                          "api: chip.set('metric','place','0','luts', 100)"
                      ],
                      "help": "Metric tracking the total FPGA LUTs used by the design as reported\nby the implementation tool. There is no standardized definition\nfor this metric across vendors, so metric comparisons can\ngenerally only be done between runs on identical tools and\ndevice families.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: FPGA LUTs",
                      "signature": null,
                      "switch": "-metric_luts 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "macros": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_macros 'place 0 100'",
                          "api: chip.set('metric','place','0','macros', 50)"
                      ],
                      "help": "Metric tracking the total number of macros in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: macros",
                      "signature": null,
                      "switch": "-metric_macros 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "memory": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_memory 'dfm 0 10e9'",
                          "api: chip.set('metric','dfm','0','memory', 10e9)"
                      ],
                      "help": "Metric tracking total peak program memory footprint on a per\nstep and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: memory",
                      "signature": null,
                      "switch": "-metric_memory 'step index <float>'",
                      "type": "float",
                      "unit": "B",
                      "value": null
                  },
                  "nets": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_nets 'place 0 100'",
                          "api: chip.set('metric','place','0','nets', 50)"
                      ],
                      "help": "Metric tracking the total number of nets in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: nets",
                      "signature": null,
                      "switch": "-metric_nets 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "overflow": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_overflow 'place 0 0'",
                          "api: chip.set('metric','place','0','overflow', 50)"
                      ],
                      "help": "Metric tracking the total number of overflow tracks for the routing\non per step and index basis. Any non-zero number suggests an over\ncongested design. To analyze where the congestion is occurring\ninspect the router log files for detailed per metal overflow\nreporting and open up the design to find routing hotspots.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: overflow",
                      "signature": null,
                      "switch": "-metric_overflow 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "peakpower": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_peakpower 'place 0 0.01'",
                          "api: chip.set('metric','place','0','peakpower', 0.01)"
                      ],
                      "help": "Metric tracking the worst case total peak power of the design specified on a per step\nand index basis. Power metric depend heavily on the method\nbeing used for extraction: dynamic vs static, workload\nspecification (vcd vs saif), power models, process/voltage/temperature.\nThe power peakpower metric tries to capture the data that would\nusually be reflected inside a datasheet given the approprate\nfootnote conditions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: peakpower",
                      "signature": null,
                      "switch": "-metric_peakpower 'step index <float>'",
                      "type": "float",
                      "unit": "mw",
                      "value": null
                  },
                  "pins": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_pins 'place 0 100'",
                          "api: chip.set('metric','place','0','pins', 50)"
                      ],
                      "help": "Metric tracking the total number of pins in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: pins",
                      "signature": null,
                      "switch": "-metric_pins 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "registers": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_registers 'place 0 100'",
                          "api: chip.set('metric','place','0','registers', 50)"
                      ],
                      "help": "Metric tracking the total number of register instances in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: registers",
                      "signature": null,
                      "switch": "-metric_registers 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "security": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_security 'place 0 100'",
                          "api: chip.set('metric','place','0','security', 100)"
                      ],
                      "help": "Metric tracking the level of security (1/vulnerability) of the design.\nA completely secure design would have a score of 100. There is no\nabsolute scale for the security metrics (like with power, area, etc)\nso the metric will be task and tool dependent.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: security",
                      "signature": null,
                      "switch": "-metric_security 'step index <float>'",
                      "type": "float",
                      "unit": "%",
                      "value": null
                  },
                  "setuppaths": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_setuppaths 'place 0 10'",
                          "api: chip.set('metric','place','0','setuppaths', 10)"
                      ],
                      "help": "Metric tracking the total number of timing paths violating setup\nconstraints.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: setuppaths",
                      "signature": null,
                      "switch": "-metric_setuppaths 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "setupslack": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_setupslack 'place 0 0.01'",
                          "api: chip.set('metric','place','0','setupslack', 0.01)"
                      ],
                      "help": "Metric tracking the worst setup slack (positive or negative) on a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: setupslack",
                      "signature": null,
                      "switch": "-metric_setupslack 'step index <float>'",
                      "type": "float",
                      "unit": "ns",
                      "value": null
                  },
                  "setuptns": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_setuptns 'place 0 0.01'",
                          "api: chip.set('metric','place','0','setuptns', 0.01)"
                      ],
                      "help": "Metric tracking the total negative setup slack (TNS) on a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: setuptns",
                      "signature": null,
                      "switch": "-metric_setuptns 'step index <float>'",
                      "type": "float",
                      "unit": "ns",
                      "value": null
                  },
                  "setupwns": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_setupwns 'place 0 0.01'",
                          "api: chip.set('metric','place','0','setupwns', 0.01)"
                      ],
                      "help": "Metric tracking the worst negative setup slack (positive values truncated to zero) on a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: setupwns",
                      "signature": null,
                      "switch": "-metric_setupwns 'step index <float>'",
                      "type": "float",
                      "unit": "ns",
                      "value": null
                  },
                  "sleeppower": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_sleeppower 'place 0 0.01'",
                          "api: chip.set('metric','place','0','sleeppower', 0.01)"
                      ],
                      "help": "Metric tracking the power consumed with some or all power rails gated off of the design specified on a per step\nand index basis. Power metric depend heavily on the method\nbeing used for extraction: dynamic vs static, workload\nspecification (vcd vs saif), power models, process/voltage/temperature.\nThe power sleeppower metric tries to capture the data that would\nusually be reflected inside a datasheet given the approprate\nfootnote conditions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: sleeppower",
                      "signature": null,
                      "switch": "-metric_sleeppower 'step index <float>'",
                      "type": "float",
                      "unit": "mw",
                      "value": null
                  },
                  "tasktime": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_tasktime 'dfm 0 10.0'",
                          "api: chip.set('metric','dfm','0','tasktime', 10.0)"
                      ],
                      "help": "Metric trakcing the total amount of time spent on a task from\nbeginning to end, including data transfers and pre/post\nprocessing.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: tasktime",
                      "signature": null,
                      "switch": "-metric_tasktime 'step index <float>'",
                      "type": "float",
                      "unit": "s",
                      "value": null
                  },
                  "totalarea": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_totalarea 'place 0 100.00'",
                          "api: chip.set('metric','place','0','totalarea', 100.00)"
                      ],
                      "help": "Metric tracking the total physical die area occupied by the design.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: totalarea",
                      "signature": null,
                      "switch": "-metric_totalarea 'step index <float>'",
                      "type": "float",
                      "unit": "um^2",
                      "value": null
                  },
                  "totaltime": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_totaltime 'dfm 0 10.0'",
                          "api: chip.set('metric','dfm','0','totaltime', 10.0)"
                      ],
                      "help": "Metric tracking the total amount of time spent from the beginning\nof the run up to and including the current step and index.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: totaltime",
                      "signature": null,
                      "switch": "-metric_totaltime 'step index <float>'",
                      "type": "float",
                      "unit": "s",
                      "value": null
                  },
                  "transistors": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_transistors 'place 0 100'",
                          "api: chip.set('metric','place','0','transistors', 50)"
                      ],
                      "help": "Metric tracking the total number of transistors in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: transistors",
                      "signature": null,
                      "switch": "-metric_transistors 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "unconstrained": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_unconstrained 'dfm 0 0'",
                          "api: chip.set('metric','dfm','0','unconstrained', 0)"
                      ],
                      "help": "Metric tracking the total number of unconstrained timing paths on a\nper step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: total unconstrained",
                      "signature": null,
                      "switch": "-metric_unconstrained 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "utilization": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_utilization 'place 0 50.00'",
                          "api: chip.set('metric','place','0','utilization', 50.00)"
                      ],
                      "help": "Metric tracking the area utilization of the design calculated as\n100 * (cellarea/totalarea).",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: area utilization",
                      "signature": null,
                      "switch": "-metric_utilization step index <float>",
                      "type": "float",
                      "unit": "%",
                      "value": null
                  },
                  "vias": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_vias 'place 0 100'",
                          "api: chip.set('metric','place','0','vias', 50)"
                      ],
                      "help": "Metric tracking the total number of vias in the design\non a per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: vias",
                      "signature": null,
                      "switch": "-metric_vias 'step index <float>'",
                      "type": "int",
                      "value": null
                  },
                  "warnings": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_warnings 'dfm 0 0'",
                          "api: chip.set('metric','dfm','0','warnings', 0)"
                      ],
                      "help": "Metric tracking the total number of warnings on a\nper step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: total warnings",
                      "signature": null,
                      "switch": "-metric_warnings 'step index <int>'",
                      "type": "int",
                      "value": null
                  },
                  "wirelength": {
                      "defvalue": null,
                      "example": [
                          "cli: -metric_wirelength 'place 0 100.0'",
                          "api: chip.set('metric','place','0','wirelength', 50.0)"
                      ],
                      "help": "Metric tracking the total wirelength of the design on a per step\nand index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Metric: wirelength",
                      "signature": null,
                      "switch": "-metric_wirelength 'step index <float>'",
                      "type": "float",
                      "unit": "um",
                      "value": null
                  }
              }
          }
      },
      "model": {
          "formal": {
              "default": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -model_formal 'smv adder.smv'",
                      "api: chip.set('model','formal','smv','adder.smv')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Filepaths to formal models specified on a per filetype basis.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Model: Formal",
                  "signature": [],
                  "switch": "-model_formal 'filetype <file>'",
                  "type": "[file]",
                  "value": []
              }
          },
          "functional": {
              "default": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -model_functional 'systemc adder.sc'",
                      "api: chip.set('model','functional','systemc','adder.sc')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Filepaths to (fast) functional models specified on a per filetype\nbasis.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Model: Functional",
                  "signature": [],
                  "switch": "-model_functional 'filetype <file>'",
                  "type": "[file]",
                  "value": []
              }
          },
          "io": {
              "default": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -model_io 'ibis top.ibs'",
                      "api: chip.set('model','io','ibis','top.ibis')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Filepaths to IO models specified on a per filetype basis.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Model: IO",
                  "signature": [],
                  "switch": "-model_io 'filetype <file>'",
                  "type": "[file]",
                  "value": []
              }
          },
          "layout": {
              "default": {
                  "default": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -model_layout 'lef 10M adder.lef'",
                          "api: chip.set('model','layout','lef','10M','adder.lef')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepaths to abstract layout views specified on a per filetype\nand per stackup basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Model: Layout",
                      "signature": [],
                      "switch": "-model_layout 'filetype stackup <file>'",
                      "type": "[file]",
                      "value": []
                  }
              }
          },
          "power": {
              "default": {
                  "default": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -model_power 'apl ss adder.ss.apl'",
                          "api: chip.set('model','power','apl','ss','adder.ss.apl')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepaths to power/current models.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Model: Power",
                      "signature": [],
                      "switch": "-model_power 'filetype corner <file>'",
                      "type": "[file]",
                      "value": []
                  }
              }
          },
          "rtl": {
              "default": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -model_rtl 'systemc adder.sc'",
                      "api: chip.set('model','rtl','systemc','adder.sc')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Filepaths to cycle accurate model specified on a per filetype\nbasis.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Model: RTL",
                  "signature": [],
                  "switch": "-model_rtl 'filetype <file>'",
                  "type": "[file]",
                  "value": []
              }
          },
          "thermal": {
              "default": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -model_thermal 'delphi adder_thermal.sp'",
                      "api: chip.set('model','thermal','delphi','adder_thermal.sp')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Filepaths to thermal models.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Model: Thermal",
                  "signature": [],
                  "switch": "-model_thermal 'filetype corner <file>'",
                  "type": "[file]",
                  "value": []
              }
          },
          "timing": {
              "default": {
                  "default": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -model_timing 'nldm-libgz ss ss.lib.gz'",
                          "api: chip.set('model','timing','nldm-ldb','ss','ss.ldb')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepaths to static timing models specified on a per filetype and\nper corner basis.  Examples of filetypes include: nldm, nldm-ldb,\nnldm-libgz, ccs, ccs-libgz, ccs-ldb, scm, scm-libgz, scm-ldb,\naocv, aocv-libgz, aocv-ldb.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Model: Timing",
                      "signature": [],
                      "switch": "-model_timing 'filetype corner <file>'",
                      "type": "[file]",
                      "value": []
                  }
              }
          }
      },
      "option": {
          "autoinstall": {
              "defvalue": "false",
              "example": [
                  "cli: -autoinstall true'",
                  "api: chip.set('option', 'autoinstall', True)"
              ],
              "help": "Enables automatic installation of missing dependencies from\nthe registry.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Option: auto install packages",
              "signature": null,
              "switch": "-autoinstall <bool>",
              "type": "bool",
              "value": "false"
          },
          "bkpt": {
              "defvalue": [],
              "example": [
                  "cli: -bkpt place",
                  "api: chip.set('option,'bkpt','place')"
              ],
              "help": "List of step stop (break) points. If the step is a TCL\nbased tool, then the breakpoints stops the flow inside the\nEDA tool. If the step is a command line tool, then the flow\ndrops into a Python interpreter.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Breakpoint list",
              "signature": [],
              "switch": "-bkpt <str>",
              "type": "[str]",
              "value": []
          },
          "builddir": {
              "defvalue": "build",
              "example": [
                  "cli: -builddir ./build_the_future",
                  "api: chip.set('option', 'builddir','./build_the_future')"
              ],
              "help": "The default build directory is in the local './build' where SC was\nexecuted. The 'builddir' parameter can be used to set an alternate\ncompilation directory path.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Build directory",
              "signature": null,
              "switch": "-builddir <dir>",
              "type": "dir",
              "value": "build"
          },
          "cfg": {
              "author": [],
              "copy": "false",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -cfg mypdk.json",
                  "api: chip.set('option','cfg','mypdk.json')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "List of filepaths to JSON formatted schema configuration\nmanifests. The files are read in automatically when using the\n'sc' command line application. In Python programs, JSON manifests\ncan be merged into the current working manifest using the\nread_manifest() method.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Configuration manifest",
              "signature": [],
              "switch": "-cfg <file>",
              "type": "[file]",
              "value": []
          },
          "clean": {
              "defvalue": "false",
              "example": [
                  "cli: -clean",
                  "api: chip.set('option','clean',True)"
              ],
              "help": "Clean up all intermediate and non essential files at the end\nof a task, leaving the following:\n\n* log file\n* replay.sh\n* inputs/\n* outputs/\n* reports/\n* autogenerated manifests\n* any files generated by schema-specified regexes\n* files specified by :keypath:`tool, <tool>, keep`",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Clean up after run",
              "signature": null,
              "switch": "-clean <bool>",
              "type": "bool",
              "value": "false"
          },
          "cmdfile": {
              "author": [],
              "copy": "false",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -f design.f",
                  "api: chip.set('option', 'cmdfile','design.f')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "Read the specified file, and act as if all text inside it was specified\nas command line parameters. Supported by most verilog simulators\nincluding Icarus and Verilator. The format of the file is not strongly\nstandardized. Support for comments and environment variables within\nthe file varies and depends on the tool used. SC simply passes on\nthe filepath toe the tool executable.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Design compilation command file",
              "signature": [],
              "switch": "-f <file>",
              "type": "[file]",
              "value": []
          },
          "copyall": {
              "defvalue": "false",
              "example": [
                  "cli: -copyall",
                  "api: chip.set('option','copyall',True)"
              ],
              "help": "Specifies that all used files should be copied into the\nbuild directory, overriding the per schema entry copy\nsettings.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Copy all inputs to build directory",
              "signature": null,
              "switch": "-copyall <bool>",
              "type": "bool",
              "value": "false"
          },
          "credentials": {
              "author": [],
              "copy": "false",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -credentials /home/user/.sc/credentials",
                  "api: chip.set('option', 'credentials','/home/user/.sc/credentials')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "Filepath to credentials used for remote processing. If the\ncredentials parameter is empty, the remote processing client program\ntries to access the \".sc/credentials\" file in the user's home\ndirectory. The file supports the following fields:\n\nuserid=<user id>\nsecret_key=<secret key used for authentication>\nserver=<ipaddr or url>",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "User credentials file",
              "signature": [],
              "switch": "-credentials <file>'",
              "type": "[file]",
              "value": []
          },
          "define": {
              "defvalue": [],
              "example": [
                  "cli: -DCFG_ASIC=1",
                  "api: chip.set('option','define','CFG_ASIC=1')"
              ],
              "help": "Symbol definition for source preprocessor.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Design pre-processor symbol",
              "signature": [],
              "switch": "-D<str>",
              "type": "[str]",
              "value": []
          },
          "entrypoint": {
              "defvalue": null,
              "example": [
                  "cli: -entrypoint top",
                  "api: chip.set('option', 'entrypoint', 'top')"
              ],
              "help": "Alternative entrypoint for compilation and\nsimulation. The default entry point is 'design'.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Program entry point",
              "signature": null,
              "switch": "-entrypoint <str>",
              "type": "str",
              "value": null
          },
          "env": {
              "default": {
                  "defvalue": null,
                  "example": [
                      "cli: -env 'PDK_HOME /disk/mypdk'",
                      "api: chip.set('option','env', 'PDK_HOME', '/disk/mypdk')"
                  ],
                  "help": "Certain tools and reference flows require global environment\nvariables to be set. These variables can be managed externally or\nspecified through the env variable.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Environment variables",
                  "signature": null,
                  "switch": "-env 'key <str>",
                  "type": "str",
                  "value": null
              }
          },
          "flow": {
              "defvalue": null,
              "example": [
                  "cli: -flow asicfow",
                  "api: chip.set('option','flow','asicflow')"
              ],
              "help": "Sets the flow for the current run. The flow name\nmust match up with a 'flow' in the flowgraph",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Flow target",
              "signature": null,
              "switch": "-flow <str>",
              "type": "str",
              "value": null
          },
          "flowcontinue": {
              "defvalue": "false",
              "example": [
                  "cli: -flowcontinue",
                  "api: chip.set('option', 'flowcontinue', True)"
              ],
              "help": "Continue executing flow after a tool logs errors. The default\nbehavior is to quit executing the flow if a task ends and the errors\nmetric is greater than 0. Note that the flow will always cease\nexecuting if the tool returns a nonzero status code.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Continue flow on error",
              "signature": null,
              "switch": "-flowcontinue",
              "type": "bool",
              "value": "false"
          },
          "frontend": {
              "defvalue": "verilog",
              "example": [
                  "cli: -frontend systemverilog",
                  "api: chip.set('option','frontend', 'systemverilog')"
              ],
              "help": "Specifies the frontend that flows should use for importing and\nprocessing source files. Default option is 'verilog', also supports\n'systemverilog' and 'chisel'. When using the Python API, this parameter\nmust be configured before calling load_target().",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Compilation frontend",
              "signature": null,
              "switch": "-frontend <frontend>",
              "type": "str",
              "value": "verilog"
          },
          "hash": {
              "defvalue": "false",
              "example": [
                  "cli: -hash",
                  "api: chip.set('option','hash',True)"
              ],
              "help": "Enables hashing of all inputs and outputs during\ncompilation. The hash values are stored in the hashvalue\nfield of the individual parameters.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Enable file hashing",
              "signature": null,
              "switch": "-hash <bool>",
              "type": "bool",
              "value": "false"
          },
          "idir": {
              "defvalue": [],
              "example": [
                  "cli: +incdir+./mylib",
                  "api: chip.set('option','idir','./mylib')"
              ],
              "help": "Search paths to look for files included in the design using\nthe ```include`` statement.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Design search paths",
              "signature": [],
              "switch": [
                  "+incdir+<dir>",
                  "-I <dir>"
              ],
              "type": "[dir]",
              "value": []
          },
          "indexlist": {
              "defvalue": [],
              "example": [
                  "cli: -indexlist 0",
                  "api: chip.set('option','indexlist','0')"
              ],
              "help": "List of indices to execute. The default is to execute all\nindices for each step of a run.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Compilation index list",
              "signature": [],
              "switch": "-indexlist <index>",
              "type": "[str]",
              "value": []
          },
          "jobincr": {
              "defvalue": "false",
              "example": [
                  "cli: -jobincr",
                  "api: chip.set('option','jobincr',True)"
              ],
              "help": "Forces an auto-update of the jobname parameter if a directory\nmatching the jobname is found in the build directory. If the\njobname does not include a trailing digit, then a the number\n'1' is added to the jobname before updating the jobname\nparameter.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Autoincrement jobname",
              "signature": null,
              "switch": "-jobincr <bool>",
              "type": "bool",
              "value": "false"
          },
          "jobinput": {
              "default": {
                  "default": {
                      "defvalue": null,
                      "example": [
                          "cli: -jobinput 'cts 0 job0'",
                          "api:  chip.set('option','jobinput','cts,'0','job0')"
                      ],
                      "help": "Specifies jobname inputs for the current run() on a per step\nand per index basis. During execution, the default behavior is to\ncopy inputs from the current job.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Input job name",
                      "signature": null,
                      "switch": "-jobinput 'step index <str>'",
                      "type": "str",
                      "value": null
                  }
              }
          },
          "jobname": {
              "defvalue": "job0",
              "example": [
                  "cli: -jobname may1",
                  "api: chip.set('option','jobname','may1')"
              ],
              "help": "Jobname during invocation of run(). The jobname combined with a\ndefined director structure (<dir>/<design>/<jobname>/<step>/<index>)\nenables multiple levels of transparent job, step, and index\nintrospection.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Job name",
              "signature": null,
              "switch": "-jobname <str>",
              "type": "str",
              "value": "job0"
          },
          "jobscheduler": {
              "defvalue": null,
              "example": [
                  "cli: -jobscheduler slurm",
                  "api: chip.set('option','jobscheduler','slurm')"
              ],
              "help": "Sets the type of job scheduler to be used for each individual\nflowgraph steps. If the parameter is undefined, the steps are executed\non the same machine that the SC was launched on. If 'slurm' is used,\nthe host running the 'sc' command must be running a 'slurmctld' daemon\nmanaging a Slurm cluster. Additionally, the build directory ('-dir')\nmust be located in shared storage which can be accessed by all hosts\nin the cluster.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Job scheduler name",
              "signature": null,
              "switch": "-jobscheduler <str>",
              "type": "str",
              "value": null
          },
          "libext": {
              "defvalue": [],
              "example": [
                  "cli: +libext+sv",
                  "api: chip.set('option','libext','sv')"
              ],
              "help": "List of file extensions that should be used for finding modules.\nFor example, if -y is specified as ./lib\", and '.v' is specified as\nlibext then the files ./lib/\\*.v \", will be searched for\nmodule matches.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Design file extensions",
              "signature": [],
              "switch": "+libext+<str>",
              "type": "[str]",
              "value": []
          },
          "loglevel": {
              "defvalue": "INFO",
              "example": [
                  "cli: -loglevel INFO",
                  "api: chip.set('option', 'loglevel', 'INFO')"
              ],
              "help": "Provides explicit control over the level of debug logging printed.\nValid entries include INFO, DEBUG, WARNING, ERROR.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Logging level",
              "signature": null,
              "switch": "-loglevel <str>",
              "type": "str",
              "value": "INFO"
          },
          "metricoff": {
              "defvalue": [],
              "example": [
                  "cli: -metricoff 'wirelength'",
                  "api: chip.set('option','metricoff','wirelength')"
              ],
              "help": "List of metrics to supress when printing out the run\nsummary.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Metric summary filter",
              "signature": [],
              "switch": "-metricoff '<str>'",
              "type": "[str]",
              "value": []
          },
          "mode": {
              "defvalue": null,
              "example": [
                  "cli: -mode asic",
                  "api: chip.set('option','mode','asic')"
              ],
              "help": "Sets the operating mode of the compiler. Valid modes are:\nasic: RTL to GDS ASIC compilation\nfpga: RTL to bitstream FPGA compilation\nsim: simulation to verify design and compilation",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Compilation mode",
              "signature": null,
              "switch": "-mode <str>",
              "type": "str",
              "value": null
          },
          "msgcontact": {
              "defvalue": [],
              "example": [
                  "cli: -msgcontact 'wile.e.coyote@acme.com'",
                  "api: chip.set('option','msgcontact','wiley@acme.com')"
              ],
              "help": "A list of phone numbers or email addresses to message\non a event event within the msg_event param. Actual\nsupport for email and phone messages is platform\ndependent.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Message contact",
              "signature": [],
              "switch": "-msgcontact <str>",
              "type": "[str]",
              "value": []
          },
          "msgevent": {
              "defvalue": [],
              "example": [
                  "cli: -msgevent export",
                  "api: chip.set('option','msgevent','export')"
              ],
              "help": "A list of steps after which to notify a recipient. For\nexample if values of syn, place, cts are entered separate\nmessages would be sent after the completion of the syn,\nplace, and cts steps.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Message event trigger",
              "signature": [],
              "switch": "-msgevent <str>",
              "type": "[str]",
              "value": []
          },
          "nodisplay": {
              "defvalue": "false",
              "example": [
                  "cli: -nodisplay",
                  "api: chip.set('option','nodisplay',True)"
              ],
              "help": "The '-nodisplay' flag prevents SiliconCompiler from\nopening GUI windows such as the final metrics report.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Headless execution",
              "signature": null,
              "switch": "-nodisplay <bool>",
              "type": "bool",
              "value": "false"
          },
          "novercheck": {
              "defvalue": "false",
              "example": [
                  "cli: -novercheck",
                  "api: chip.set('option','novercheck',True)"
              ],
              "help": "Disables strict version checking on all invoked tools if True.\nThe list of supported version numbers is defined in the\n'version' parameter in the 'eda' dictionary for each tool.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Disable version checking",
              "signature": null,
              "switch": "-novercheck <bool>",
              "type": "bool",
              "value": "false"
          },
          "optmode": {
              "defvalue": "O0",
              "example": [
                  "cli: -O3",
                  "api: chip.set('option','optmode','O3')"
              ],
              "help": "The compiler has modes to prioritize run time and ppa. Modes\ninclude.\n\n(O0) = Exploration mode for debugging setup\n(O1) = Higher effort and better PPA than O0\n(O2) = Higher effort and better PPA than O1\n(O3) = Signoff quality. Better PPA and higher run times than O2\n(O4-O98) = Reserved (compiler/target dependent)\n(O99) = Experimental highest possible effort, may be unstable",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Optimization mode",
              "signature": null,
              "switch": "-O<str>",
              "type": "str",
              "value": "O0"
          },
          "param": {
              "default": {
                  "defvalue": null,
                  "example": [
                      "cli: -param 'N 64'",
                      "api: chip.set('option','param','N','64')"
                  ],
                  "help": "Sets a top verilog level design module parameter. The value\nis limited to basic data literals. The parameter override is\npassed into tools such as Verilator and Yosys. The parameters\nsupport Verilog integer literals (64'h4, 2'b0, 4) and strings.\nName of the top level module to compile.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Design parameter",
                  "signature": null,
                  "switch": "-param 'name <str>'",
                  "type": "str",
                  "value": null
              }
          },
          "pdk": {
              "defvalue": null,
              "example": [
                  "cli: -pdk asap7",
                  "api: chip.set('option','pdk','asap7')"
              ],
              "help": "Sets the pdk for the current run. The pdk name\nmust match up with a 'pdk' loaded with load_pdk.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "PDK target",
              "signature": null,
              "switch": "-pdk <str>",
              "type": "str",
              "value": null
          },
          "quiet": {
              "defvalue": "false",
              "example": [
                  "cli: -quiet",
                  "api: chip.set('option','quiet',True)"
              ],
              "help": "The -quiet option forces all steps to print to a log file.\nThis can be useful with Modern EDA tools which print\nsignificant content to the screen.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Quiet execution",
              "signature": null,
              "switch": "-quiet <bool>",
              "type": "bool",
              "value": "false"
          },
          "registry": {
              "defvalue": [],
              "example": [
                  "cli: -registry '~/myregistry'",
                  "api: chip.set('option','registry','~/myregistry')"
              ],
              "help": "List of Silicon Unified Packager (SUP) registry directories.\nDirectories can be local file system folders or\npublicly available registries served up over http. The naming\nconvention for registry packages is:\n<name>/<name>-<version>.json(.<gz>)?",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Option: package registry",
              "signature": [],
              "switch": "-registry <dir>",
              "type": "[dir]",
              "value": []
          },
          "relax": {
              "defvalue": "false",
              "example": [
                  "cli: -relax",
                  "api: chip.set('option','relax',True)"
              ],
              "help": "Specifies that tools should be lenient and suppress some\nwarnings that may or may not indicate design issues.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Relax RTL linting",
              "signature": null,
              "switch": "-relax <bool>",
              "type": "bool",
              "value": "false"
          },
          "remote": {
              "defvalue": "false",
              "example": [
                  "cli: -remote",
                  "api: chip.set('option','remote', True)"
              ],
              "help": "Sends job for remote processing if set to true. The remote\noption requires a credentials file to be placed in the home\ndirectory. Fore more information, see the credentials\nparameter.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Enable remote processing",
              "signature": null,
              "switch": "-remote <bool>",
              "type": "bool",
              "value": "false"
          },
          "resume": {
              "defvalue": "false",
              "example": [
                  "cli: -resume",
                  "api: chip.set('option','resume',True)"
              ],
              "help": "If results exist for current job, then don't re-run any steps that\nhad at least one index run successfully. Useful for debugging a\nflow that failed partway through.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Resume build",
              "signature": null,
              "switch": "-resume <bool>",
              "type": "bool",
              "value": "false"
          },
          "scpath": {
              "defvalue": [],
              "example": [
                  "cli: -scpath '/home/$USER/sclib'",
                  "api: chip.set('option', 'scpath','/home/$USER/sclib')"
              ],
              "help": "Specifies python modules paths for target import.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Search path",
              "signature": [],
              "switch": "-scpath <dir>",
              "type": "[dir]",
              "value": []
          },
          "show": {
              "defvalue": "false",
              "example": [
                  "cli: -show",
                  "api: chip.set('option','show',True)"
              ],
              "help": "Specifies that the final hardware layout should be\nshown after the compilation has been completed. The\nfinal layout and tool used to display the layout is\nflow dependent.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Show layout",
              "signature": null,
              "switch": "-show <bool>",
              "type": "bool",
              "value": "false"
          },
          "showtool": {
              "default": {
                  "defvalue": null,
                  "example": [
                      "cli: -showtool 'gds klayout'",
                      "api: chip.set('option','showtool','gds','klayout')"
                  ],
                  "help": "Selects the tool to use by the show function for displaying\nthe specified filetype.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Select data display tool",
                  "signature": null,
                  "switch": "-showtool 'filetype <tool>'",
                  "type": "str",
                  "value": null
              }
          },
          "skipall": {
              "defvalue": "false",
              "example": [
                  "cli: -skipall",
                  "api: chip.set('option','skipall',True)"
              ],
              "help": "Skips the execution of all tools in run(), enabling a quick\ncheck of tool and setup without having to run through each\nstep of a flow to completion.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Skip all tasks",
              "signature": null,
              "switch": "-skipall <bool>",
              "type": "bool",
              "value": "false"
          },
          "skipcheck": {
              "defvalue": "false",
              "example": [
                  "cli: -skipcheck",
                  "api: chip.set('option','skipcheck',True)"
              ],
              "help": "Bypasses the strict runtime manifest check. Can be used for\naccelerating initial bringup of tool/flow/pdk/libs targets.\nThe flag should not be used for production compilation.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Skip manifest check",
              "signature": null,
              "switch": "-skipcheck <bool>",
              "type": "bool",
              "value": "false"
          },
          "skipstep": {
              "defvalue": [],
              "example": [
                  "cli: -skipstep lvs",
                  "api: chip.set('option','skipstep','lvs')"
              ],
              "help": "List of steps to skip during execution.The default is to\nexecute all steps  defined in the flow graph.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Skip step list",
              "signature": [],
              "switch": "-skipstep <str>",
              "type": "[str]",
              "value": []
          },
          "steplist": {
              "defvalue": [],
              "example": [
                  "cli: -steplist 'import'",
                  "api: chip.set('option','steplist','import')"
              ],
              "help": "List of steps to execute. The default is to execute all steps\ndefined in the flow graph.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Compilation step list",
              "signature": [],
              "switch": "-steplist <step>",
              "type": "[str]",
              "value": []
          },
          "target": {
              "defvalue": null,
              "example": [
                  "cli: -target freepdk45_demo",
                  "api: chip.set('option','target','freepdk45_demo')"
              ],
              "help": "Sets a target module to be used for compilation. The target\nmodule must set up all paramaters needed. The target module\nmay load multiple flows and libraries.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Compilation target",
              "signature": null,
              "switch": "-target <str>",
              "type": "str",
              "value": null
          },
          "trace": {
              "defvalue": "false",
              "example": [
                  "cli: -trace",
                  "api: chip.set('option','trace',True)"
              ],
              "help": "Enables debug tracing during compilation and/or runtime.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Enable debug traces",
              "signature": null,
              "switch": "-trace <bool>",
              "type": "bool",
              "value": "false"
          },
          "track": {
              "defvalue": "false",
              "example": [
                  "cli: -track",
                  "api: chip.set('option','track',True)"
              ],
              "help": "Turns on tracking of all 'record' parameters during each\ntask. Tracking will result in potentially sensitive data\nbeing recorded in the manifest so only turn on this feature\nif you have control of the final manifest.",
              "lock": "false",
              "notes": null,
              "require": "all",
              "scope": "job",
              "shorthelp": "Enable provenance tracking",
              "signature": null,
              "switch": "-track <bool>",
              "type": "bool",
              "value": "false"
          },
          "vlib": {
              "author": [],
              "copy": "false",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -v './mylib.v'",
                  "api: chip.set('option', 'vlib','./mylib.v')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "List of library files to be read in. Modules found in the\nlibraries are not interpreted as root modules.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Design libraries",
              "signature": [],
              "switch": "-v <file>",
              "type": "[file]",
              "value": []
          },
          "ydir": {
              "defvalue": [],
              "example": [
                  "cli: -y './mylib'",
                  "api: chip.set('option','ydir','./mylib')"
              ],
              "help": "Search paths to look for verilog modules found in the the\nsource list. The import engine will look for modules inside\nfiles with the specified +libext+ param suffix.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Design module search paths",
              "signature": [],
              "switch": "-y <dir>",
              "type": "[dir]",
              "value": []
          }
      },
      "output": {
          "default": {
              "author": [],
              "copy": "false",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -output 'verilog hello_world.v'",
                  "api: chip.set(output,'verilog','hello_world.v')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "List of output files specifed by type. The filetype name must\nalign with the parameter names within the flow and tool setup\nscripts. Examples of acceptable file types include python, c,\nsystemc, verilog, vhdl, netlist, def, gds, gerber, saif, sdc,\nsaif, vcd, spef, sdf.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Output files",
              "signature": [],
              "switch": "-output 'filetype <file>'",
              "type": "[file]",
              "value": []
          }
      },
      "package": {
          "author": {
              "default": {
                  "email": {
                      "defvalue": null,
                      "example": [
                          "cli: -package_author_email 'wiley wiley@acme.com'",
                          "api: chip.set('package','author','wiley','email','wiley@acme.com')"
                      ],
                      "help": "Package author email provided with full name as key and\nemail as value.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Package: author email",
                      "signature": null,
                      "switch": "-package_author_email 'userid <str>'",
                      "type": "str",
                      "value": null
                  },
                  "location": {
                      "defvalue": null,
                      "example": [
                          "cli: -package_author_location 'wiley wiley@acme.com'",
                          "api: chip.set('package','author','wiley','location','wiley@acme.com')"
                      ],
                      "help": "Package author location provided with full name as key and\nlocation as value.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Package: author location",
                      "signature": null,
                      "switch": "-package_author_location 'userid <str>'",
                      "type": "str",
                      "value": null
                  },
                  "name": {
                      "defvalue": null,
                      "example": [
                          "cli: -package_author_name 'wiley wiley@acme.com'",
                          "api: chip.set('package','author','wiley','name','wiley@acme.com')"
                      ],
                      "help": "Package author name provided with full name as key and\nname as value.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Package: author name",
                      "signature": null,
                      "switch": "-package_author_name 'userid <str>'",
                      "type": "str",
                      "value": null
                  },
                  "organization": {
                      "defvalue": null,
                      "example": [
                          "cli: -package_author_organization 'wiley wiley@acme.com'",
                          "api: chip.set('package','author','wiley','organization','wiley@acme.com')"
                      ],
                      "help": "Package author organization provided with full name as key and\norganization as value.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Package: author organization",
                      "signature": null,
                      "switch": "-package_author_organization 'userid <str>'",
                      "type": "str",
                      "value": null
                  },
                  "publickey": {
                      "defvalue": null,
                      "example": [
                          "cli: -package_author_publickey 'wiley wiley@acme.com'",
                          "api: chip.set('package','author','wiley','publickey','wiley@acme.com')"
                      ],
                      "help": "Package author publickey provided with full name as key and\npublickey as value.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Package: author publickey",
                      "signature": null,
                      "switch": "-package_author_publickey 'userid <str>'",
                      "type": "str",
                      "value": null
                  },
                  "username": {
                      "defvalue": null,
                      "example": [
                          "cli: -package_author_username 'wiley wiley@acme.com'",
                          "api: chip.set('package','author','wiley','username','wiley@acme.com')"
                      ],
                      "help": "Package author username provided with full name as key and\nusername as value.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "Package: author username",
                      "signature": null,
                      "switch": "-package_author_username 'userid <str>'",
                      "type": "str",
                      "value": null
                  }
              }
          },
          "dependency": {
              "default": {
                  "defvalue": [],
                  "example": [
                      "cli: -package_dependency 'hello 1.0'",
                      "api: chip.set('package','dependency','hello','1.0')"
                  ],
                  "help": "Package dependencies specified as a key value pair.\nVersions shall follow the semver standard.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: version dependancies",
                  "signature": [],
                  "switch": "-package_dependency 'module <str>'",
                  "type": "[str]",
                  "value": []
              }
          },
          "depgraph": {
              "default": {
                  "defvalue": [],
                  "example": [
                      "cli: -package_depgraph 'top (cpu,1.0.1)'",
                      "api: chip.set('package','depgraph','top',('cpu','1.0.1'))"
                  ],
                  "help": "List of Silicon Unified Packager (SUP) dependencies\nused by the design specified on a per module basis a\nlist of string tuples ('name','version').",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package dependency list",
                  "signature": [],
                  "switch": "-package_depgraph 'module <(str,str)>'",
                  "type": "[(str,str)]",
                  "value": []
              }
          },
          "description": {
              "defvalue": null,
              "example": [
                  "cli: -package_description 'Yet another cpu'",
                  "api: chip.set('package','description','Yet another cpu')"
              ],
              "help": "Package short one line description for package\nmanagers and summary reports.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: description",
              "signature": null,
              "switch": "-package_description <str>",
              "type": "str",
              "value": null
          },
          "doc": {
              "datasheet": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_datasheet datasheet.pdf",
                      "api: chip.set('package','doc',datasheet,'datasheet.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of datasheet documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: datasheet document",
                  "signature": [],
                  "switch": "-package_doc_datasheet <str",
                  "type": "[file]",
                  "value": []
              },
              "homepage": {
                  "defvalue": null,
                  "example": [
                      "cli: -package_doc_homepage index.html",
                      "api: chip.set('package','doc', 'homepage','index.html')"
                  ],
                  "help": "Package documentation homepage. Filepath to design docs homepage.\nComplex designs can can include a long non standard list of\ndocuments dependent.  A single html entry point can be used to\npresent an organized documentation dashboard to the designer.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: documentation homepage",
                  "signature": null,
                  "switch": "-package_doc_homepage <str>",
                  "type": "str",
                  "value": null
              },
              "quickstart": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_quickstart quickstart.pdf",
                      "api: chip.set('package','doc',quickstart,'quickstart.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of quickstart documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: quickstart document",
                  "signature": [],
                  "switch": "-package_doc_quickstart <str",
                  "type": "[file]",
                  "value": []
              },
              "reference": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_reference reference.pdf",
                      "api: chip.set('package','doc',reference,'reference.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of reference documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: reference document",
                  "signature": [],
                  "switch": "-package_doc_reference <str",
                  "type": "[file]",
                  "value": []
              },
              "releasenotes": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_releasenotes releasenotes.pdf",
                      "api: chip.set('package','doc',releasenotes,'releasenotes.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of releasenotes documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: releasenotes document",
                  "signature": [],
                  "switch": "-package_doc_releasenotes <str",
                  "type": "[file]",
                  "value": []
              },
              "signoff": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_signoff signoff.pdf",
                      "api: chip.set('package','doc',signoff,'signoff.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of signoff documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: signoff document",
                  "signature": [],
                  "switch": "-package_doc_signoff <str",
                  "type": "[file]",
                  "value": []
              },
              "testplan": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_testplan testplan.pdf",
                      "api: chip.set('package','doc',testplan,'testplan.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of testplan documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: testplan document",
                  "signature": [],
                  "switch": "-package_doc_testplan <str",
                  "type": "[file]",
                  "value": []
              },
              "tutorial": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_tutorial tutorial.pdf",
                      "api: chip.set('package','doc',tutorial,'tutorial.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of tutorial documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: tutorial document",
                  "signature": [],
                  "switch": "-package_doc_tutorial <str",
                  "type": "[file]",
                  "value": []
              },
              "userguide": {
                  "author": [],
                  "copy": "false",
                  "date": [],
                  "defvalue": [],
                  "example": [
                      "cli: -package_doc_userguide userguide.pdf",
                      "api: chip.set('package','doc',userguide,'userguide.pdf')"
                  ],
                  "filehash": [],
                  "hashalgo": "sha256",
                  "help": "Package list of userguide documents.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "Package: userguide document",
                  "signature": [],
                  "switch": "-package_doc_userguide <str",
                  "type": "[file]",
                  "value": []
              }
          },
          "homepage": {
              "defvalue": null,
              "example": [
                  "cli: -package_homepage index.html",
                  "api: chip.set('package','homepage','index.html')"
              ],
              "help": "Package homepage.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: project homepage",
              "signature": null,
              "switch": "-package_homepage <str>",
              "type": "str",
              "value": null
          },
          "keyword": {
              "defvalue": null,
              "example": [
                  "cli: -package_keyword cpu",
                  "api: chip.set('package','keyword','cpu')"
              ],
              "help": "Package keyword(s) used to characterize package.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: keyword",
              "signature": null,
              "switch": "-package_keyword <str>",
              "type": "str",
              "value": null
          },
          "license": {
              "defvalue": [],
              "example": [
                  "cli: -package_license 'Apache-2.0'",
                  "api: chip.set('package','license','Apache-2.0')"
              ],
              "help": "Package list of SPDX license identifiers.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: license identifiers",
              "signature": [],
              "switch": "-package_license <str>",
              "type": "[str]",
              "value": []
          },
          "licensefile": {
              "author": [],
              "copy": "false",
              "date": [],
              "defvalue": [],
              "example": [
                  "cli: -package_licensefile './LICENSE'",
                  "api: chip.set('package','licensefile','./LICENSE')"
              ],
              "filehash": [],
              "hashalgo": "sha256",
              "help": "Package list of license files for to be\napplied in cases when a SPDX identifier is not available.\n(eg. proprietary licenses).list of SPDX license identifiers.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: license files",
              "signature": [],
              "switch": "-package_licensefile <file>",
              "type": "[file]",
              "value": []
          },
          "location": {
              "defvalue": [],
              "example": [
                  "cli: -package_location 'mars'",
                  "api: chip.set('package','location','mars')"
              ],
              "help": "Package country of origin specified as standardized\ninternational country codes. The field can be left blank\nif the location is unknown or global.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: location",
              "signature": [],
              "switch": "-package_location <file>",
              "type": "[str]",
              "value": []
          },
          "name": {
              "defvalue": null,
              "example": [
                  "cli: -package_name yac",
                  "api: chip.set('package','name','yac')"
              ],
              "help": "Package name.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: name",
              "signature": null,
              "switch": "-package_name <str>",
              "type": "str",
              "value": null
          },
          "organization": {
              "defvalue": [],
              "example": [
                  "cli: -package_organization 'humanity'",
                  "api: chip.set('package','organization','humanity')"
              ],
              "help": "Package sponsoring organization. The field can be left\nblank if not applicable.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: sponsoring organization",
              "signature": [],
              "switch": "-package_organization <str>",
              "type": "[str]",
              "value": []
          },
          "publickey": {
              "defvalue": null,
              "example": [
                  "cli: -package_publickey '6EB695706EB69570'",
                  "api: chip.set('package','publickey','6EB695706EB69570')"
              ],
              "help": "Package public project key.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: public key",
              "signature": null,
              "switch": "-package_publickey <str>",
              "type": "str",
              "value": null
          },
          "repo": {
              "defvalue": [],
              "example": [
                  "cli: -package_repo 'git@github.com:aolofsson/oh.git'",
                  "api: chip.set('package','repo','git@github.com:aolofsson/oh.git')"
              ],
              "help": "Package IP address to source code repository.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: code repository",
              "signature": [],
              "switch": "-package_repo <str>",
              "type": "[str]",
              "value": []
          },
          "target": {
              "defvalue": [],
              "example": [
                  "cli: -package_target 'asicflow_freepdk45'",
                  "api: chip.set('package','target','asicflow_freepdk45')"
              ],
              "help": "Package list of qualified compilation targets.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: qualified targets",
              "signature": [],
              "switch": "-package_target <str>",
              "type": "[str]",
              "value": []
          },
          "version": {
              "defvalue": null,
              "example": [
                  "cli: -package_version 1.0",
                  "api: chip.set('package','version','1.0')"
              ],
              "help": "Package version. Can be a branch, tag, commit hash,\nor a semver compatible version.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "global",
              "shorthelp": "Package: version",
              "signature": null,
              "switch": "-package_version <str>",
              "type": "str",
              "value": null
          }
      },
      "pdk": {
          "default": {
              "aprtech": {
                  "default": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_aprtech 'asap7 openroad M10 12t lef tech.lef'",
                                      "api: chip.set('pdk','asap7','aprtech','openroad','M10','12t','lef','tech.lef')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Technology file containing setup information needed to enable DRC clean APR\nfor the specified stackup, libarch, and format. The 'libarch' specifies the\nlibrary architecture (e.g. library height). For example a PDK with support\nfor 9 and 12 track libraries might have 'libarchs' called 9t and 12t.\nThe standard filetype for specifying place and route design rules for a\nprocess node is through a 'lef' format technology file. The\n'filetype' used in the aprtech is used by the tool specific APR TCL scripts\nto set up the technology parameters. Some tools may require additional\nfiles beyond the tech.lef file. Examples of extra file types include\nantenna, tracks, tapcell, viarules, em.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: APR technology files",
                                  "signature": [],
                                  "switch": "-pdk_aprtech 'pdkname tool stackup libarch filetype <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  }
              },
              "d0": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_d0 'asap7 0.1'",
                      "api:  chip.set('pdk', 'asap7', 'd0', 0.1)"
                  ],
                  "help": "Process defect density (d0) expressed as random defects per cm^2. The\nvalue is used to calculate yield losses as a function of area, which in\nturn affects the chip full factory costs. Two yield models are\nsupported: Poisson (default), and Murphy. The Poisson based yield is\ncalculated as dy = exp(-area * d0/100). The Murphy based yield is\ncalculated as dy = ((1-exp(-area * d0/100))/(area * d0/100))^2.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: process defect density",
                  "signature": null,
                  "switch": "-pdk_d0 'pdkname <float>'",
                  "type": "float",
                  "value": null
              },
              "density": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_density 'asap7 100e6'",
                      "api:  chip.set('pdk', 'asap7', 'density', 10e6)"
                  ],
                  "help": "Approximate logic density expressed as # transistors / mm^2\ncalculated as:\n0.6 * (Nand2 Transistor Count) / (Nand2 Cell Area) +\n0.4 * (Register Transistor Count) / (Register Cell Area)\nThe value is specified for a fixed standard cell library within a node\nand will differ depending on the library vendor, library track height\nand library type. The value can be used to to normalize the effective\ndensity reported for the design across different process nodes. The\nvalue can be derived from a variety of sources, including the PDK DRM,\nlibrary LEFs, conference presentations, and public analysis.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: transistor density",
                  "signature": null,
                  "switch": "-pdk_density 'pdkname <float>'",
                  "type": "float",
                  "value": null
              },
              "devmodel": {
                  "default": {
                      "default": {
                          "default": {
                              "author": [],
                              "copy": "false",
                              "date": [],
                              "defvalue": [],
                              "example": [
                                  "cli: -pdk_devmodel 'asap7 xyce spice M10 asap7.sp'",
                                  "api: chip.set('pdk','asap7','devmodel','xyce','spice','M10','asap7.sp')"
                              ],
                              "filehash": [],
                              "hashalgo": "sha256",
                              "help": "List of filepaths to PDK device models for different simulation\npurposes and for different tools. Examples of device model types\ninclude spice, aging, electromigration, radiation. An example of a\n'spice' tool is xyce. Device models are specified on a per metal stack\nbasis. Process nodes with a single device model across all stacks will\nhave a unique parameter record per metal stack pointing to the same\ndevice model file.  Device types and tools are dynamic entries\nthat depend on the tool setup and device technology. Pseud-standardized\ndevice types include spice, em (electromigration), and aging.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: device models",
                              "signature": [],
                              "switch": "-pdk_devmodel 'pdkname tool simtype stackup <file>'",
                              "type": "[file]",
                              "value": []
                          }
                      }
                  }
              },
              "directory": {
                  "default": {
                      "default": {
                          "default": {
                              "defvalue": [],
                              "example": [
                                  "cli: -pdk_directory 'asap7 xyce rfmodel M10 rftechdir'",
                                  "api: chip.set('pdk','asap7','directory','xyce','rfmodel','M10','rftechdir')"
                              ],
                              "help": "List of named directories specified on a per tool and per stackup basis.\nThe parameter should only be used for specifying files that are\nnot directly  supported by the SiliconCompiler PDK schema.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: special directory",
                              "signature": [],
                              "switch": "-pdk_directory 'pdkname tool key stackup <file>'",
                              "type": "[dir]",
                              "value": []
                          }
                      }
                  }
              },
              "display": {
                  "default": {
                      "default": {
                          "author": [],
                          "copy": "false",
                          "date": [],
                          "defvalue": [],
                          "example": [
                              "cli: -pdk_display 'asap7 klayout M10 display.lyt'",
                              "api: chip.set('pdk','asap7','display','klayout','M10','display.cfg')"
                          ],
                          "filehash": [],
                          "hashalgo": "sha256",
                          "help": "Display configuration files describing colors and pattern schemes for\nall layers in the PDK. The display configuration file is entered on a\nstackup and tool basis.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "global",
                          "shorthelp": "PDK: display file",
                          "signature": [],
                          "switch": "-pdk_display 'pdkname tool stackup <file>'",
                          "type": "[file]",
                          "value": []
                      }
                  }
              },
              "doc": {
                  "datasheet": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_datasheet 'asap7 datasheet.pdf'",
                          "api: chip.set('pdk','asap7','doc',datasheet,'datasheet.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to datasheet document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: datasheet",
                      "signature": [],
                      "switch": "-pdk_doc_datasheet 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "homepage": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_homepage 'asap7 index.html'",
                          "api: chip.set('pdk','asap7','doc','homepage','index.html')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to PDK docs homepage. Modern PDKs can include tens or\nhundreds of individual documents. A single html entry point can\nbe used to present an organized documentation dashboard to the\ndesigner.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: documentation homepage",
                      "signature": [],
                      "switch": "-pdk_doc_homepage 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "install": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_install 'asap7 install.pdf'",
                          "api: chip.set('pdk','asap7','doc',install,'install.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to install document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: install",
                      "signature": [],
                      "switch": "-pdk_doc_install 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "quickstart": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_quickstart 'asap7 quickstart.pdf'",
                          "api: chip.set('pdk','asap7','doc',quickstart,'quickstart.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to quickstart document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: quickstart",
                      "signature": [],
                      "switch": "-pdk_doc_quickstart 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "reference": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_reference 'asap7 reference.pdf'",
                          "api: chip.set('pdk','asap7','doc',reference,'reference.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to reference document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: reference",
                      "signature": [],
                      "switch": "-pdk_doc_reference 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "releasenotes": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_releasenotes 'asap7 releasenotes.pdf'",
                          "api: chip.set('pdk','asap7','doc',releasenotes,'releasenotes.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to releasenotes document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: releasenotes",
                      "signature": [],
                      "switch": "-pdk_doc_releasenotes 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "tutorial": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_tutorial 'asap7 tutorial.pdf'",
                          "api: chip.set('pdk','asap7','doc',tutorial,'tutorial.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to tutorial document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: tutorial",
                      "signature": [],
                      "switch": "-pdk_doc_tutorial 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  },
                  "userguide": {
                      "author": [],
                      "copy": "false",
                      "date": [],
                      "defvalue": [],
                      "example": [
                          "cli: -pdk_doc_userguide 'asap7 userguide.pdf'",
                          "api: chip.set('pdk','asap7','doc',userguide,'userguide.pdf')"
                      ],
                      "filehash": [],
                      "hashalgo": "sha256",
                      "help": "Filepath to userguide document.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: userguide",
                      "signature": [],
                      "switch": "-pdk_doc_userguide 'pdkname <file>'",
                      "type": "[file]",
                      "value": []
                  }
              },
              "drc": {
                  "runset": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_drc_runset 'asap7 magic M10 basic $PDK/drc.rs'",
                                      "api: chip.set('pdk', 'asap7','drc','runset','magic','M10','basic','$PDK/drc.rs')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Runset files for DRC task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: DRC runset files",
                                  "signature": [],
                                  "switch": "-pdk_drc_runset 'pdkname tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  },
                  "waiver": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_drc_waiver 'asap7 magic M10 basic $PDK/drc.txt'",
                                      "api: chip.set('pdk', 'asap7','drc','waiver','magic','M10','basic','$PDK/drc.txt')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Waiver files for DRC task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: DRC waiver files",
                                  "signature": [],
                                  "switch": "-pdk_drc_waiver 'tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  }
              },
              "edgemargin": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_edgemargin 'asap7 1'",
                      "api:  chip.set('pdk', 'asap7', 'edgemargin', 1)"
                  ],
                  "help": "Keep-out distance/margin from the edge inwards. The edge\nis prone to chipping and need special treatment that preclude\nplacement of designs in this area. The edge value is used to\ncalculate effective units per wafer/panel and full factory cost.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: wafer edge keep-out margin",
                  "signature": null,
                  "switch": "-pdk_edgemargin 'pdkname <float>'",
                  "type": "float",
                  "unit": "mm",
                  "value": null
              },
              "erc": {
                  "runset": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_erc_runset 'asap7 magic M10 basic $PDK/erc.rs'",
                                      "api: chip.set('pdk', 'asap7','erc','runset','magic','M10','basic','$PDK/erc.rs')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Runset files for ERC task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: ERC runset files",
                                  "signature": [],
                                  "switch": "-pdk_erc_runset 'pdkname tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  },
                  "waiver": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_erc_waiver 'asap7 magic M10 basic $PDK/erc.txt'",
                                      "api: chip.set('pdk', 'asap7','erc','waiver','magic','M10','basic','$PDK/erc.txt')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Waiver files for ERC task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: ERC waiver files",
                                  "signature": [],
                                  "switch": "-pdk_erc_waiver 'tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  }
              },
              "file": {
                  "default": {
                      "default": {
                          "default": {
                              "author": [],
                              "copy": "false",
                              "date": [],
                              "defvalue": [],
                              "example": [
                                  "cli: -pdk_file 'asap7 xyce spice M10 asap7.sp'",
                                  "api: chip.set('pdk','asap7','file','xyce','spice','M10','asap7.sp')"
                              ],
                              "filehash": [],
                              "hashalgo": "sha256",
                              "help": "List of named files specified on a per tool and per stackup basis.\nThe parameter should only be used for specifying files that are\nnot directly  supported by the SiliconCompiler PDK schema.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: special file",
                              "signature": [],
                              "switch": "-pdk_file 'pdkname tool key stackup <file>'",
                              "type": "[file]",
                              "value": []
                          }
                      }
                  }
              },
              "fill": {
                  "runset": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_fill_runset 'asap7 magic M10 basic $PDK/fill.rs'",
                                      "api: chip.set('pdk', 'asap7','fill','runset','magic','M10','basic','$PDK/fill.rs')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Runset files for FILL task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: FILL runset files",
                                  "signature": [],
                                  "switch": "-pdk_fill_runset 'pdkname tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  },
                  "waiver": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_fill_waiver 'asap7 magic M10 basic $PDK/fill.txt'",
                                      "api: chip.set('pdk', 'asap7','fill','waiver','magic','M10','basic','$PDK/fill.txt')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Waiver files for FILL task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: FILL waiver files",
                                  "signature": [],
                                  "switch": "-pdk_fill_waiver 'tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  }
              },
              "foundry": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_foundry 'asap7 virtual'",
                      "api:  chip.set('pdk', 'asap7', 'foundry', 'virtual')"
                  ],
                  "help": "Name of foundry corporation. Examples include intel, gf, tsmc,\nsamsung, skywater, virtual. The 'virtual' keyword is reserved for\nsimulated non-manufacturable processes.",
                  "lock": "false",
                  "notes": null,
                  "require": "asic",
                  "scope": "global",
                  "shorthelp": "PDK: foundry name",
                  "signature": null,
                  "switch": "-pdk_foundry 'pdkname <str>'",
                  "type": "str",
                  "value": null
              },
              "grid": {
                  "default": {
                      "default": {
                          "adj": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_adj 'asap7 M10 m2 0.5'",
                                  "api: chip.set('pdk','asap7','grid','M10','m2','adj','0.5')"
                              ],
                              "help": "Defines the routing resources adjustments for the design on a per layer\nbasis. The value is expressed as a fraction from 0 to 1. A value of\n0.5 reduces the routing resources by 50%. If not defined, 100%\nrouting resource utilization is permitted.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid resource adjustment",
                              "signature": null,
                              "switch": "-pdk_grid_adj 'pdkname stackup layer <float>'",
                              "type": "float",
                              "value": null
                          },
                          "cap": {
                              "default": {
                                  "defvalue": null,
                                  "example": [
                                      "cli: -pdk_grid_cap 'asap7 M10 m2 fast 0.2'",
                                      "api: chip.set('pdk','asap7','grid','M10','m2','cap','fast','0.2')"
                                  ],
                                  "help": "Unit capacitance of a wire defined by the grid width and spacing values\nin the 'grid' structure specified on a per\nstackup, metal, and corner basis. As a rough rule of thumb, this value\ntends to stay around 0.2ff/um. This number should only be used for\nreality confirmation. Accurate analysis should use the PEX models.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: routing grid unit capacitance",
                                  "signature": null,
                                  "switch": "-pdk_grid_cap 'pdkname stackup layer corner <float>''",
                                  "type": "float",
                                  "unit": "ff/um",
                                  "value": null
                              }
                          },
                          "dir": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_dir 'asap7 M10 m1 horizontal'",
                                  "api: chip.set('pdk','asap7','grid','M10','m1','dir','horizontal')"
                              ],
                              "help": "Preferred routing direction specified on a per stackup\nand per metal basis. Valid routing directions are horizontal\nand vertical.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid preferred direction",
                              "signature": null,
                              "switch": "-pdk_grid_dir 'pdkname stackup layer <str>'",
                              "type": "str",
                              "value": null
                          },
                          "name": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_name 'asap7 M10 metal1 m1'",
                                  "api: chip.set('pdk','asap7','grid','M10','metal1','name','m1')"
                              ],
                              "help": "Maps PDK metal names to the SC standardized layer stack\nstarting with m1 as the lowest routing layer and ending\nwith m<n> as the highest routing layer. The map is\nspecified on a per metal stack basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid name map",
                              "signature": null,
                              "switch": "-pdk_grid_name 'pdkname stackup layer <str>'",
                              "type": "str",
                              "value": null
                          },
                          "res": {
                              "default": {
                                  "defvalue": null,
                                  "example": [
                                      "cli: -pdk_grid_res 'asap7 M10 m2 fast 0.2'",
                                      "api: chip.set('pdk','asap7','grid','M10','m2','res','fast','0.2')"
                                  ],
                                  "help": "Resistance of a wire defined by the grid width and spacing values\nin the 'grid' structure specified as ohms/um on a per\nstackup, metal, and corner basis. The parameter is only meant to be\nused as a sanity check and for coarse design planning. Accurate\nanalysis should use the TCAD PEX models.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: routing grid unit resistance",
                                  "signature": null,
                                  "switch": "-pdk_grid_res 'pdkname stackup layer corner <float>''",
                                  "type": "float",
                                  "unit": "ohm/um",
                                  "value": null
                              }
                          },
                          "tcr": {
                              "default": {
                                  "defvalue": null,
                                  "example": [
                                      "cli: -pdk_grid_tcr 'asap7 M10 m2 fast 0.2'",
                                      "api: chip.set('pdk','asap7','grid','M10','m2','tcr','fast','0.2')"
                                  ],
                                  "help": "Temperature coefficient of resistance of the wire defined by the grid\nwidth and spacing values in the 'grid' structure on a per stackup,\nlayer, and corner basis. The number is only meant to be used as a\nsanity check and for coarse design planning. Accurate analysis\nshould use the PEX models.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: routing grid temperature coefficient",
                                  "signature": null,
                                  "switch": "-pdk_grid_tcr 'pdkname stackup layer corner <float>'",
                                  "type": "float",
                                  "unit": "%/degree(T)",
                                  "value": null
                              }
                          },
                          "xoffset": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_xoffset 'asap7 M10 m2 0.5'",
                                  "api: chip.set('pdk','asap7','grid','M10','m2','xoffset','0.5')"
                              ],
                              "help": "Defines the grid offset of a vertical metal layer specified on a per\nstackup and per metal basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid vertical wire offset",
                              "signature": null,
                              "switch": "-pdk_grid_xoffset 'pdkname stackup layer <float>'",
                              "type": "float",
                              "unit": "um",
                              "value": null
                          },
                          "xpitch": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_xpitch 'asap7 M10 m1 0.5'",
                                  "api: chip.set('pdk', 'asap7','grid','M10','m1','xpitch','0.5')"
                              ],
                              "help": "Defines the routing pitch for vertical wires on a per stackup and\nper metal basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid vertical wire pitch",
                              "signature": null,
                              "switch": "-pdk_grid_xpitch 'pdkname stackup layer <float>'",
                              "type": "float",
                              "unit": "um",
                              "value": null
                          },
                          "yoffset": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_yoffset 'asap7 M10 m2 0.5'",
                                  "api: chip.set('pdk', 'asap7','grid','M10','m2','yoffset','0.5')"
                              ],
                              "help": "Defines the grid offset of a horizontal metal layer specified on a per\nstackup and per metal basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid horizontal wire offset",
                              "signature": null,
                              "switch": "-pdk_grid_yoffset 'pdkname stackup layer <float>'",
                              "type": "float",
                              "unit": "um",
                              "value": null
                          },
                          "ypitch": {
                              "defvalue": null,
                              "example": [
                                  "cli: -pdk_grid_ypitch 'asap7 M10 m1 0.5'",
                                  "api: chip.set('pdk','asap7','grid','M10','m1','ypitch','0.5')"
                              ],
                              "help": "Defines the routing pitch for horizontal wires on a per stackup and\nper metal basis.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: routing grid horizontal wire pitch",
                              "signature": null,
                              "switch": "-pdk_grid_ypitch 'pdkname stackup layer <float>'",
                              "type": "float",
                              "unit": "um",
                              "value": null
                          }
                      }
                  }
              },
              "hscribe": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_hscribe 'asap7 0.1'",
                      "api:  chip.set('pdk', 'asap7', 'hscribe', 0.1)"
                  ],
                  "help": "Width of the horizontal scribe line used during die separation.\nThe process is generally completed using a mechanical saw, but can be\ndone through combinations of mechanical saws, lasers, wafer thinning,\nand chemical etching in more advanced technologies. The value is used\nto calculate effective dies per wafer and full factory cost.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: horizontal scribe line width",
                  "signature": null,
                  "switch": "-pdk_hscribe 'pdkname <float>'",
                  "type": "float",
                  "unit": "mm",
                  "value": null
              },
              "layermap": {
                  "default": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_layermap 'asap7 klayout db gds M10 asap7.map'",
                                      "api: chip.set('pdk','asap7','layermap','klayout','db','gds','M10','asap7.map')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Files describing input/output mapping for streaming layout data from\none format to another. A foundry PDK will include an official layer\nlist for all user entered and generated layers supported in the GDS\naccepted by the foundry for processing, but there is no standardized\nlayer definition format that can be read and written by all EDA tools.\nTo ensure mask layer matching, key/value type mapping files are needed\nto convert EDA databases to/from GDS and to convert between different\ntypes of EDA databases. Layer maps are specified on a per metal\nstackup basis. The 'src' and 'dst' can be names of SC supported tools\nor file formats (like 'gds').",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: layer map file",
                                  "signature": [],
                                  "switch": "-pdk_layermap 'pdkname tool src dst stackup <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  }
              },
              "lvs": {
                  "runset": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_lvs_runset 'asap7 magic M10 basic $PDK/lvs.rs'",
                                      "api: chip.set('pdk', 'asap7','lvs','runset','magic','M10','basic','$PDK/lvs.rs')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Runset files for LVS task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: LVS runset files",
                                  "signature": [],
                                  "switch": "-pdk_lvs_runset 'pdkname tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  },
                  "waiver": {
                      "default": {
                          "default": {
                              "default": {
                                  "author": [],
                                  "copy": "false",
                                  "date": [],
                                  "defvalue": [],
                                  "example": [
                                      "cli: -pdk_lvs_waiver 'asap7 magic M10 basic $PDK/lvs.txt'",
                                      "api: chip.set('pdk', 'asap7','lvs','waiver','magic','M10','basic','$PDK/lvs.txt')"
                                  ],
                                  "filehash": [],
                                  "hashalgo": "sha256",
                                  "help": "Waiver files for LVS task.",
                                  "lock": "false",
                                  "notes": null,
                                  "require": null,
                                  "scope": "global",
                                  "shorthelp": "PDK: LVS waiver files",
                                  "signature": [],
                                  "switch": "-pdk_lvs_waiver 'tool stackup name <file>'",
                                  "type": "[file]",
                                  "value": []
                              }
                          }
                      }
                  }
              },
              "node": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_node 'asap7 130'",
                      "api:  chip.set('pdk', 'asap7', 'node', 130)"
                  ],
                  "help": "Approximate relative minimum dimension of the process target specified\nin nanometers. The parameter is required for flows and tools that\nleverage the value to drive technology dependent synthesis and APR\noptimization. Node examples include 180, 130, 90, 65, 45, 32, 22 14,\n10, 7, 5, 3.",
                  "lock": "false",
                  "notes": null,
                  "require": "asic",
                  "scope": "global",
                  "shorthelp": "PDK: process node",
                  "signature": null,
                  "switch": "-pdk_node 'pdkname <float>'",
                  "type": "float",
                  "value": null
              },
              "panelsize": {
                  "defvalue": [],
                  "example": [
                      "cli: -pdk_panelsize 'asap7 (45.72,60.96)'",
                      "api:  chip.set('pdk', 'asap7', 'panelsize', (45.72,60.96))"
                  ],
                  "help": "List of panel sizes supported in the manufacturing process.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: panel size",
                  "signature": [],
                  "switch": "-pdk_panelsize 'pdkname <float>'",
                  "type": "[(float,float)]",
                  "unit": "mm",
                  "value": []
              },
              "pexmodel": {
                  "default": {
                      "default": {
                          "default": {
                              "author": [],
                              "copy": "false",
                              "date": [],
                              "defvalue": [],
                              "example": [
                                  "cli: -pdk_pexmodel 'asap7 fastcap M10 max wire.mod'",
                                  "api: chip.set('pdk','asap7','pexmodel','fastcap','M10','max','wire.mod')"
                              ],
                              "filehash": [],
                              "hashalgo": "sha256",
                              "help": "List of filepaths to PDK wire TCAD models used during automated\nsynthesis, APR, and signoff verification. Pexmodels are specified on\na per metal stack basis. Corner values depend on the process being\nused, but typically include nomenclature such as min, max, nominal.\nFor exact names, refer to the DRM. Pexmodels are generally not\nstandardized and specified on a per tool basis. An example of pexmodel\ntype is 'fastcap'.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: parasitic TCAD models",
                              "signature": [],
                              "switch": "-pdk_pexmodel 'pdkname tool stackup corner <file>'",
                              "type": "[file]",
                              "value": []
                          }
                      }
                  }
              },
              "stackup": {
                  "defvalue": [],
                  "example": [
                      "cli: -pdk_stackup 'asap7 2MA4MB2MC'",
                      "api: chip.add('pdk', 'asap7','stackup','2MA4MB2MC')"
                  ],
                  "help": "List of all metal stackups offered in the process node. Older process\nnodes may only offer a single metal stackup, while advanced nodes\noffer a large but finite list of metal stacks with varying combinations\nof metal line pitches and thicknesses. Stackup naming is unique to a\nfoundry, but is generally a long string or code. For example, a 10\nmetal stackup with two 1x wide, four 2x wide, and 4x wide metals,\nmight be identified as 2MA4MB2MC, where MA, MB, and MC denote wiring\nlayers with different properties (thickness, width, space). Each\nstackup will come with its own set of routing technology files and\nparasitic models specified in the pdk_pexmodel and pdk_aprtech\nparameters.",
                  "lock": "false",
                  "notes": null,
                  "require": "asic",
                  "scope": "global",
                  "shorthelp": "PDK: metal stackups",
                  "signature": [],
                  "switch": "-pdk_stackup 'pdkname <str>'",
                  "type": "[str]",
                  "value": []
              },
              "thickness": {
                  "default": {
                      "defvalue": null,
                      "example": [
                          "cli: -pdk_thickness 'asap7 2MA4MB2MC 1.57'",
                          "api:  chip.set('pdk', 'asap7', 'thickness', '2MA4MB2MC', 1.57)"
                      ],
                      "help": "Thickness of a manfuatured unit specified on a per stackup basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "global",
                      "shorthelp": "PDK: unit thickness",
                      "signature": null,
                      "switch": "-pdk_thickness 'pdkname stackup <float>'",
                      "type": "float",
                      "unit": "mm",
                      "value": null
                  }
              },
              "unitcost": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_unitcost 'asap7 10000'",
                      "api:  chip.set('pdk', 'asap7', 'unitcost', 10000)"
                  ],
                  "help": "Raw cost per unit shipped by the factory, not accounting for yield\nloss.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: unit cost",
                  "signature": null,
                  "switch": "-pdk_unitcost 'pdkname <float>'",
                  "type": "float",
                  "unit": "USD",
                  "value": null
              },
              "var": {
                  "default": {
                      "default": {
                          "default": {
                              "defvalue": [],
                              "example": [
                                  "cli: -pdk_var 'asap7 xyce modeltype M10 bsim4'",
                                  "api: chip.set('pdk','asap7','var','xyce','modeltype','M10','bsim4')"
                              ],
                              "help": "List of key/value strings specified on a per tool and per stackup basis.\nThe parameter should only be used for specifying variables that are\nnot directly  supported by the SiliconCompiler PDK schema.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "global",
                              "shorthelp": "PDK: special variable",
                              "signature": [],
                              "switch": "-pdk_var 'pdkname tool stackup key <str>'",
                              "type": "[str]",
                              "value": []
                          }
                      }
                  }
              },
              "version": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_version 'asap7 1.0'",
                      "api:  chip.set('pdk', 'asap7', 'version', '1.0')"
                  ],
                  "help": "Alphanumeric string specifying the version of the PDK. Verification of\ncorrect PDK and IP versions is a hard ASIC tapeout require in all\ncommercial foundries. The version number can be used for design manifest\ntracking and tapeout checklists.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: version",
                  "signature": null,
                  "switch": "-pdk_version 'pdkname <str>'",
                  "type": "str",
                  "value": null
              },
              "vscribe": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_vscribe 'asap7 0.1'",
                      "api:  chip.set('pdk', 'asap7', 'vscribe', 0.1)"
                  ],
                  "help": " Width of the vertical scribe line used during die separation.\nThe process is generally completed using a mechanical saw, but can be\ndone through combinations of mechanical saws, lasers, wafer thinning,\nand chemical etching in more advanced technologies. The value is used\nto calculate effective dies per wafer and full factory cost.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "global",
                  "shorthelp": "PDK: vertical scribe line width",
                  "signature": null,
                  "switch": "-pdk_vscribe 'pdkname <float>'",
                  "type": "float",
                  "unit": "mm",
                  "value": null
              },
              "wafersize": {
                  "defvalue": null,
                  "example": [
                      "cli: -pdk_wafersize 'asap7 300'",
                      "api:  chip.set('pdk', 'asap7', 'wafersize', 300)"
                  ],
                  "help": "Wafer diameter used in wafer based manufacturing process. The standard diameter\nfor leading edge manufacturing is 300mm. For older process technologies\nand specialty fabs, smaller diameters such as 200, 100, 125, 100 are common.\nThe value is used to calculate dies per wafer and full factory chip costs.",
                  "lock": "false",
                  "notes": null,
                  "require": "asic",
                  "scope": "global",
                  "shorthelp": "PDK: wafer size",
                  "signature": null,
                  "switch": "-pdk_wafersize 'pdkname <float>'",
                  "type": "float",
                  "unit": "mm",
                  "value": null
              }
          }
      },
      "record": {
          "default": {
              "default": {
                  "arch": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_arch 'dfm 0 <x86_64>'",
                          "api: chip.set('record','dfm','0','arch', <x86_64>)"
                      ],
                      "help": "Record tracking the hardware architecture per step and index basis. (x86_64, rv64imafdc)",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: hardware architecture",
                      "signature": null,
                      "switch": "-record_arch 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "distro": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_distro 'dfm 0 <ubuntu>'",
                          "api: chip.set('record','dfm','0','distro', <ubuntu>)"
                      ],
                      "help": "Record tracking the distro name per step and index basis. (ubuntu, redhat, centos)",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: distro name",
                      "signature": null,
                      "switch": "-record_distro 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "endtime": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_endtime 'dfm 0 <2021-09-06 12:20:20>'",
                          "api: chip.set('record','dfm','0','endtime', <2021-09-06 12:20:20>)"
                      ],
                      "help": "Record tracking the end time per step and index basis. Time is reported in the ISO 8601 format YYYY-MM-DD HR:MIN:SEC",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: end time",
                      "signature": null,
                      "switch": "-record_endtime 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "ipaddr": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_ipaddr 'dfm 0 <<addr>>'",
                          "api: chip.set('record','dfm','0','ipaddr', <<addr>>)"
                      ],
                      "help": "Record tracking the IP address per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: IP address",
                      "signature": null,
                      "switch": "-record_ipaddr 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "kernelversion": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_kernelversion 'dfm 0 <5.11.0-34-generic>'",
                          "api: chip.set('record','dfm','0','kernelversion', <5.11.0-34-generic>)"
                      ],
                      "help": "Record tracking the O/S kernel version per step and index basis. Used for platforms that support a distinction\nbetween os kernels and os distributions.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: O/S kernel version",
                      "signature": null,
                      "switch": "-record_kernelversion 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "macaddr": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_macaddr 'dfm 0 <<addr>>'",
                          "api: chip.set('record','dfm','0','macaddr', <<addr>>)"
                      ],
                      "help": "Record tracking the MAC address per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: MAC address",
                      "signature": null,
                      "switch": "-record_macaddr 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "machine": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_machine 'dfm 0 <carbon>'",
                          "api: chip.set('record','dfm','0','machine', <carbon>)"
                      ],
                      "help": "Record tracking the machine name per step and index basis. (myhost, localhost, ...",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: machine name",
                      "signature": null,
                      "switch": "-record_machine 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "osversion": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_osversion 'dfm 0 <20.04.1-Ubuntu>'",
                          "api: chip.set('record','dfm','0','osversion', <20.04.1-Ubuntu>)"
                      ],
                      "help": "Record tracking the O/S version per step and index basis. Since there is not standard version system for operating\nsystems, extracting information from is platform dependent.\nFor Linux based operating systems, the 'osversion' is the\nversion of the distro.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: O/S version",
                      "signature": null,
                      "switch": "-record_osversion 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "platform": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_platform 'dfm 0 <linux>'",
                          "api: chip.set('record','dfm','0','platform', <linux>)"
                      ],
                      "help": "Record tracking the platform name per step and index basis. (linux, windows, freebsd)",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: platform name",
                      "signature": null,
                      "switch": "-record_platform 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "publickey": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_publickey 'dfm 0 <<key>>'",
                          "api: chip.set('record','dfm','0','publickey', <<key>>)"
                      ],
                      "help": "Record tracking the public key per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: public key",
                      "signature": null,
                      "switch": "-record_publickey 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "region": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_region 'dfm 0 <US Gov Boston>'",
                          "api: chip.set('record','dfm','0','region', <US Gov Boston>)"
                      ],
                      "help": "Record tracking the cloud region per step and index basis. Recommended naming methodology:\n\n* local: node is the local machine\n* onprem: node in on-premises IT infrastructure\n* public: generic public cloud\n* govcloud: generic US government cloud\n* <region>: cloud and entity specific region string name",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: cloud region",
                      "signature": null,
                      "switch": "-record_region 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "scversion": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_scversion 'dfm 0 <1.0>'",
                          "api: chip.set('record','dfm','0','scversion', <1.0>)"
                      ],
                      "help": "Record tracking the software version per step and index basis. Version number for the SiliconCompiler software.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: software version",
                      "signature": null,
                      "switch": "-record_scversion 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "starttime": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_starttime 'dfm 0 <2021-09-06 12:20:20>'",
                          "api: chip.set('record','dfm','0','starttime', <2021-09-06 12:20:20>)"
                      ],
                      "help": "Record tracking the start time per step and index basis. Time is reported in the ISO 8601 format YYYY-MM-DD HR:MIN:SEC",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: start time",
                      "signature": null,
                      "switch": "-record_starttime 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "toolargs": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_toolargs 'dfm 0 <-I include/ foo.v>'",
                          "api: chip.set('record','dfm','0','toolargs', <-I include/ foo.v>)"
                      ],
                      "help": "Record tracking the tool CLI arguments per step and index basis. Arguments passed to tool via CLI.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: tool CLI arguments",
                      "signature": null,
                      "switch": "-record_toolargs 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "toolpath": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_toolpath 'dfm 0 </usr/bin/openroad>'",
                          "api: chip.set('record','dfm','0','toolpath', </usr/bin/openroad>)"
                      ],
                      "help": "Record tracking the tool path per step and index basis. Full path to tool executable used to run this\ntask.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: tool path",
                      "signature": null,
                      "switch": "-record_toolpath 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "toolversion": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_toolversion 'dfm 0 <1.0>'",
                          "api: chip.set('record','dfm','0','toolversion', <1.0>)"
                      ],
                      "help": "Record tracking the tool version per step and index basis. The tool version captured correspnds to the 'tool'\nparameter within the 'eda' dictionary.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: tool version",
                      "signature": null,
                      "switch": "-record_toolversion 'step index <str>'",
                      "type": "str",
                      "value": null
                  },
                  "userid": {
                      "defvalue": null,
                      "example": [
                          "cli: -record_userid 'dfm 0 <wiley>'",
                          "api: chip.set('record','dfm','0','userid', <wiley>)"
                      ],
                      "help": "Record tracking the userid per step and index basis.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Record: userid",
                      "signature": null,
                      "switch": "-record_userid 'step index <str>'",
                      "type": "str",
                      "value": null
                  }
              }
          }
      },
      "schemaversion": {
          "defvalue": "0.8.0",
          "example": [
              "api: chip.get('schemaversion')"
          ],
          "help": "SiliconCompiler schema version number.",
          "lock": "true",
          "notes": null,
          "require": "all",
          "scope": "global",
          "shorthelp": "Schema version number",
          "signature": null,
          "switch": "-schemaversion <str>",
          "type": "str",
          "value": "0.8.0"
      },
      "tool": {
          "default": {
              "continue": {
                  "defvalue": "false",
                  "example": [
                      "cli: -tool_continue 'verilator true'",
                      "api: chip.set('tool','verilator','continue', true)"
                  ],
                  "help": "Directs tool to continue operating even if errors are\nencountered.",
                  "lock": "false",
                  "notes": null,
                  "require": "all",
                  "scope": "job",
                  "shorthelp": "Tool: continue-on-error option",
                  "signature": null,
                  "switch": "-tool_continue 'tool <bool>'",
                  "type": "bool",
                  "value": "false"
              },
              "env": {
                  "default": {
                      "default": {
                          "default": {
                              "defvalue": null,
                              "example": [
                                  "cli: -tool_env 'openroad cts 0 MYVAR 42'",
                                  "api: chip.set('tool','openroad','env','cts','0','MYVAR','42')"
                              ],
                              "help": "Environment variables to set for individual tasks. Keys and values\nshould be set in accordance with the tool's documentation. Most\ntools do not require extra environment variables to function.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: environment variables",
                              "signature": null,
                              "switch": "-tool_env 'tool step index name <str>'",
                              "type": "str",
                              "value": null
                          }
                      }
                  }
              },
              "exe": {
                  "defvalue": null,
                  "example": [
                      "cli: -tool_exe 'openroad openroad'",
                      "api:  chip.set('tool','openroad','exe','openroad')"
                  ],
                  "help": "Tool executable name.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: executable name",
                  "signature": null,
                  "switch": "-tool_exe 'tool <str>'",
                  "type": "str",
                  "value": null
              },
              "format": {
                  "defvalue": null,
                  "example": [
                      "cli: -tool_format 'yosys tcl'",
                      "api: chip.set('tool','yosys','format','tcl')"
                  ],
                  "help": "File format for tool manifest handoff. Supported formats are tcl,\nyaml, and json.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: manifest file format",
                  "signature": null,
                  "switch": "-tool_format 'tool <file>'",
                  "type": "str",
                  "value": null
              },
              "input": {
                  "default": {
                      "default": {
                          "author": [],
                          "copy": "false",
                          "date": [],
                          "defvalue": [],
                          "example": [
                              "cli: -tool_input 'openroad place 0 oh_add.def'",
                              "api: chip.set('tool','openroad','input','place','0','oh_add.def')"
                          ],
                          "filehash": [],
                          "hashalgo": "sha256",
                          "help": "List of data files to be copied from previous flowgraph steps 'output'\ndirectory. The list of steps to copy files from is defined by the\nlist defined by the dictionary key ['flowgraph', step, index, 'input'].\nAll files must be available for flow to continue. If a file\nis missing, the program exists on an error.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: input files",
                          "signature": [],
                          "switch": "-tool_input 'tool step index <str>'",
                          "type": "[file]",
                          "value": []
                      }
                  }
              },
              "keep": {
                  "default": {
                      "default": {
                          "defvalue": [],
                          "example": [
                              "cli: -tool_keep 'surelog import 0 slp_all'",
                              "api: chip.set('tool','surelog','script','import','0','slpp_all')"
                          ],
                          "help": "Names of additional files and directories in the work directory that\nshould be kept when :keypath:`option, clean` is true.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: files to keep",
                          "signature": [],
                          "switch": "-tool_keep 'tool step index <str>'",
                          "type": "[str]",
                          "value": []
                      }
                  }
              },
              "licenseserver": {
                  "default": {
                      "defvalue": [],
                      "example": [
                          "cli: -tool_licenseserver 'atool ACME_LICENSE 1700@server'",
                          "api: chip.set('tool','atool','licenseserver','ACME_LICENSE','1700@server')"
                      ],
                      "help": "Defines a set of tool specific environment variables used by the executables\nthat depend on license key servers to control access. For multiple servers,\nseparate each server by a 'colon'. The named license variable are read at\nruntime (run()) and the environment variables are set.",
                      "lock": "false",
                      "notes": null,
                      "require": null,
                      "scope": "job",
                      "shorthelp": "Tool: license servers",
                      "signature": [],
                      "switch": "-tool_licenseserver 'name key <str>'",
                      "type": "[str]",
                      "value": []
                  }
              },
              "option": {
                  "default": {
                      "default": {
                          "defvalue": [],
                          "example": [
                              "cli: -tool_option 'openroad cts 0 -no_init'",
                              "api: chip.set('tool','openroad','option','cts','0','-no_init')"
                          ],
                          "help": "List of command line options for the tool executable, specified on\na per tool and per step basis. Options must not include spaces.\nFor multiple argument options, each option is a separate list element.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: executable options",
                          "signature": [],
                          "switch": "-tool_option 'tool step index <str>'",
                          "type": "[str]",
                          "value": []
                      }
                  }
              },
              "output": {
                  "default": {
                      "default": {
                          "author": [],
                          "copy": "false",
                          "date": [],
                          "defvalue": [],
                          "example": [
                              "cli: -tool_output 'openroad place 0 oh_add.def'",
                              "api: chip.set('tool','openroad','output','place','0','oh_add.def')"
                          ],
                          "filehash": [],
                          "hashalgo": "sha256",
                          "help": "List of data files to be copied from previous flowgraph steps 'output'\ndirectory. The list of steps to copy files from is defined by the\nlist defined by the dictionary key ['flowgraph', step, index, 'output'].\nAll files must be available for flow to continue. If a file\nis missing, the program exists on an error.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: output files",
                          "signature": [],
                          "switch": "-tool_output 'tool step index <str>'",
                          "type": "[file]",
                          "value": []
                      }
                  }
              },
              "path": {
                  "defvalue": null,
                  "example": [
                      "cli: -tool_path 'openroad /usr/local/bin'",
                      "api:  chip.set('tool','openroad','path','/usr/local/bin')"
                  ],
                  "help": "File system path to tool executable. The path is prepended to the\nsystem PATH environment variable for batch and interactive runs. The\npath parameter can be left blank if the 'exe' is already in the\nenvironment search path.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: executable path",
                  "signature": null,
                  "switch": "-tool_path 'tool <dir>'",
                  "type": "dir",
                  "value": null
              },
              "refdir": {
                  "default": {
                      "default": {
                          "defvalue": [],
                          "example": [
                              "cli: -tool_refdir 'yosys syn 0 ./myref'",
                              "api:  chip.set('tool','yosys','refdir','syn','0','./myref')"
                          ],
                          "help": "Path to directories containing reference flow scripts, specified\non a per step and index basis.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: script directory",
                          "signature": [],
                          "switch": "-tool_refdir 'tool step index <dir>'",
                          "type": "[dir]",
                          "value": []
                      }
                  }
              },
              "regex": {
                  "default": {
                      "default": {
                          "default": {
                              "defvalue": [],
                              "example": [
                                  "cli: -tool_regex 'openroad place 0 error -v ERROR'",
                                  "api: chip.set('tool','openroad','regex','place','0','error','-v ERROR')"
                              ],
                              "help": "A list of piped together grep commands. Each entry represents a set\nof command line arguments for grep including the regex pattern to\nmatch. Starting with the first list entry, each grep output is piped\ninto the following grep command in the list. Supported grep options\ninclude ``-v`` and ``-e``. Patterns starting with \"-\" should be\ndirectly preceeded by the ``-e`` option. The following example\nillustrates the concept.\n\nUNIX grep:\n\n.. code-block:: bash\n\n    $ grep WARNING place.log | grep -v \"bbox\" > place.warnings\n\nSiliconCompiler::\n\n    chip.set('tool', 'openroad', 'regex', 'place', '0', 'warnings', [\"WARNING\", \"-v bbox\"])",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: regex filter",
                              "signature": [],
                              "switch": "-tool_regex 'tool step index suffix <str>'",
                              "type": "[str]",
                              "value": []
                          }
                      }
                  }
              },
              "report": {
                  "default": {
                      "default": {
                          "default": {
                              "author": [],
                              "copy": "false",
                              "date": [],
                              "defvalue": [],
                              "example": [
                                  "cli: -tool_report 'openroad place 0 holdtns place.log'",
                                  "api: chip.set('tool','openroad','report','syn','0','holdtns','place.log')"
                              ],
                              "filehash": [],
                              "hashalgo": "sha256",
                              "help": "List of report files associated with a specific 'metric'. The file path\nspecified is relative to the run directory of the current task.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: report files",
                              "signature": [],
                              "switch": "-tool_report 'tool step index metric <str>'",
                              "type": "[file]",
                              "value": []
                          }
                      }
                  }
              },
              "require": {
                  "default": {
                      "default": {
                          "defvalue": [],
                          "example": [
                              "cli: -tool_require 'openroad cts 0 design'",
                              "api: chip.set('tool','openroad','require','cts','0','design')"
                          ],
                          "help": "List of keypaths to required tool parameters. The list is used\nby check() to verify that all parameters have been set up before\nstep execution begins.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: parameter requirements",
                          "signature": [],
                          "switch": "-tool_require 'tool step index <str>'",
                          "type": "[str]",
                          "value": []
                      }
                  }
              },
              "script": {
                  "default": {
                      "default": {
                          "author": [],
                          "copy": "false",
                          "date": [],
                          "defvalue": [],
                          "example": [
                              "cli: -tool_script 'yosys syn 0 syn.tcl'",
                              "api: chip.set('tool','yosys','script','syn','0','syn.tcl')"
                          ],
                          "filehash": [],
                          "hashalgo": "sha256",
                          "help": "Path to the entry script called by the executable specified\non a per tool and per step basis.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: entry script",
                          "signature": [],
                          "switch": "-tool_script 'tool step index <file>'",
                          "type": "[file]",
                          "value": []
                      }
                  }
              },
              "stderr": {
                  "default": {
                      "default": {
                          "destination": {
                              "defvalue": "log",
                              "example": [
                                  "cli: -tool_stderr_destination 'ghdl import 0 log'",
                                  "api: chip.set('tool','ghdl','stderr','import','0','destination','log')"
                              ],
                              "help": "Defines where to direct the output generated over stderr.\nSupported options are:\nnone: the stream generated to STDERR is ignored\nlog: the generated stream is stored in <step>.<suffix>; if not in quiet mode,\nit is additionally dumped to the display output: the generated stream is\nstored in outputs/<design>.<suffix>",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: Destination for stderr",
                              "signature": null,
                              "switch": "-tool_stderr_destination 'tool step index [log|output|none]'",
                              "type": "str",
                              "value": "log"
                          },
                          "suffix": {
                              "defvalue": "log",
                              "example": [
                                  "cli: -tool_stderr_suffix 'ghdl import 0 log'",
                                  "api: chip.set('tool','ghdl','stderr','import','0','suffix','log')"
                              ],
                              "help": "Specifies the file extension for the content redirected from stderr.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: File suffix for redirected stderr",
                              "signature": null,
                              "switch": "-tool_stderr_suffix 'tool step index <str>'",
                              "type": "str",
                              "value": "log"
                          }
                      }
                  }
              },
              "stdout": {
                  "default": {
                      "default": {
                          "destination": {
                              "defvalue": "log",
                              "example": [
                                  "cli: -tool_stdout_destination 'ghdl import 0 log'",
                                  "api: chip.set('tool','ghdl','stdout','import','0','destination','log')"
                              ],
                              "help": "Defines where to direct the output generated over stdout.\nSupported options are:\nnone: the stream generated to STDOUT is ignored\nlog: the generated stream is stored in <step>.<suffix>; if not in quiet mode,\nit is additionally dumped to the display output: the generated stream is stored\nin outputs/<design>.<suffix>",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: Destination for stdout",
                              "signature": null,
                              "switch": "-tool_stdout_destination 'tool step index [log|output|none]'",
                              "type": "str",
                              "value": "log"
                          },
                          "suffix": {
                              "defvalue": "log",
                              "example": [
                                  "cli: -tool_stdout_suffix 'ghdl import 0 log'",
                                  "api: chip.set('tool','ghdl','stdout','import','0','suffix','log')"
                              ],
                              "help": "Specifies the file extension for the content redirected from stdout.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: File suffix for redirected stdout",
                              "signature": null,
                              "switch": "-tool_stdout_suffix 'tool step index <str>'",
                              "type": "str",
                              "value": "log"
                          }
                      }
                  }
              },
              "threads": {
                  "default": {
                      "default": {
                          "defvalue": null,
                          "example": [
                              "cli: -tool_threads 'magic drc 0 64'",
                              "api: chip.set('tool','magic','threads','drc','0','64')"
                          ],
                          "help": "Thread parallelism to use for execution specified on a per tool and per\nstep basis. If not specified, SC queries the operating system and sets\nthe threads based on the maximum thread count supported by the\nhardware.",
                          "lock": "false",
                          "notes": null,
                          "require": null,
                          "scope": "job",
                          "shorthelp": "Tool: thread parallelism",
                          "signature": null,
                          "switch": "-tool_threads 'tool step index <int>'",
                          "type": "int",
                          "value": null
                      }
                  }
              },
              "var": {
                  "default": {
                      "default": {
                          "default": {
                              "defvalue": [],
                              "example": [
                                  "cli: -tool_variable 'openroad cts 0 myvar 42'",
                                  "api: chip.set('tool','openroad','var','cts','0','myvar','42')"
                              ],
                              "help": "Tool script variables specified as key value pairs. Variable\nnames and value types must match the name and type of tool and reference\nscript consuming the variable.",
                              "lock": "false",
                              "notes": null,
                              "require": null,
                              "scope": "job",
                              "shorthelp": "Tool: script variables",
                              "signature": [],
                              "switch": "-tool_variable 'tool step index key <str>'",
                              "type": "[str]",
                              "value": []
                          }
                      }
                  }
              },
              "vendor": {
                  "defvalue": null,
                  "example": [
                      "cli: -tool_vendor 'yosys yosys'",
                      "api: chip.set('tool','yosys','vendor','yosys')"
                  ],
                  "help": "Name of the tool vendor. Parameter can be used to set vendor\nspecific technology variables in the PDK and libraries. For\nopen source projects, the project name should be used in\nplace of vendor.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: vendor",
                  "signature": null,
                  "switch": "-tool_vendor 'tool <str>'",
                  "type": "str",
                  "value": null
              },
              "version": {
                  "defvalue": [],
                  "example": [
                      "cli: -tool_version 'openroad >=v2.0'",
                      "api:  chip.set('tool','openroad','version','>=v2.0')"
                  ],
                  "help": "List of acceptable versions of the tool executable to be used. Each\nentry in this list must be a version specifier as described by Python\n`PEP-440 <https://peps.python.org/pep-0440/#version-specifiers>`_.\nDuring task execution, the tool is called with the 'vswitch' to\ncheck the runtime executable version. If the version of the system\nexecutable is not allowed by any of the specifiers in 'version',\nthen the job is halted pre-execution. For backwards compatibility,\nentries that do not conform to the standard will be interpreted as a\nversion with an '==' specifier. This check can be disabled by\nsetting 'novercheck' to True.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: version",
                  "signature": [],
                  "switch": "-tool_version 'tool <str>'",
                  "type": "[str]",
                  "value": []
              },
              "vswitch": {
                  "defvalue": [],
                  "example": [
                      "cli: -tool_vswitch 'openroad -version'",
                      "api:  chip.set('tool','openroad','vswitch','-version')"
                  ],
                  "help": "Command line switch to use with executable used to print out\nthe version number. Common switches include -v, -version,\n--version. Some tools may require extra flags to run in batch mode.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: executable version switch",
                  "signature": [],
                  "switch": "-tool_vswitch 'tool <str>'",
                  "type": "[str]",
                  "value": []
              },
              "warningoff": {
                  "defvalue": [],
                  "example": [
                      "cli: -tool_warningoff 'verilator COMBDLY'",
                      "api: chip.set('tool','verilator','warningoff','COMBDLY')"
                  ],
                  "help": "A list of EDA warnings for which printing should be suppressed.\nGenerally this is done on a per design basis after review has\ndetermined that warning can be safely ignored The code for turning\noff warnings can be found in the specific tool reference manual.",
                  "lock": "false",
                  "notes": null,
                  "require": null,
                  "scope": "job",
                  "shorthelp": "Tool: warning filter",
                  "signature": [],
                  "switch": "-tool_warningoff 'tool <str>'",
                  "type": "[str]",
                  "value": []
              }
          }
      },
      "unit": {
          "capacitance": {
              "defvalue": "pf",
              "example": [
                  "cli: -unit_capacitance 'pf'",
                  "api: chip.set('unit','capacitance',pf)"
              ],
              "help": "Units used for capacitance when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: capacitance",
              "signature": null,
              "switch": "-unit_capacitance '<str>'",
              "type": "str",
              "value": "pf"
          },
          "current": {
              "defvalue": "ma",
              "example": [
                  "cli: -unit_current 'ma'",
                  "api: chip.set('unit','current',ma)"
              ],
              "help": "Units used for current when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: current",
              "signature": null,
              "switch": "-unit_current '<str>'",
              "type": "str",
              "value": "ma"
          },
          "energy": {
              "defvalue": "pj",
              "example": [
                  "cli: -unit_energy 'pj'",
                  "api: chip.set('unit','energy',pj)"
              ],
              "help": "Units used for energy when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: energy",
              "signature": null,
              "switch": "-unit_energy '<str>'",
              "type": "str",
              "value": "pj"
          },
          "inductance": {
              "defvalue": "nh",
              "example": [
                  "cli: -unit_inductance 'nh'",
                  "api: chip.set('unit','inductance',nh)"
              ],
              "help": "Units used for inductance when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: inductance",
              "signature": null,
              "switch": "-unit_inductance '<str>'",
              "type": "str",
              "value": "nh"
          },
          "length": {
              "defvalue": "um",
              "example": [
                  "cli: -unit_length 'um'",
                  "api: chip.set('unit','length',um)"
              ],
              "help": "Units used for length when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: length",
              "signature": null,
              "switch": "-unit_length '<str>'",
              "type": "str",
              "value": "um"
          },
          "mass": {
              "defvalue": "g",
              "example": [
                  "cli: -unit_mass 'g'",
                  "api: chip.set('unit','mass',g)"
              ],
              "help": "Units used for mass when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: mass",
              "signature": null,
              "switch": "-unit_mass '<str>'",
              "type": "str",
              "value": "g"
          },
          "power": {
              "defvalue": "mw",
              "example": [
                  "cli: -unit_power 'mw'",
                  "api: chip.set('unit','power',mw)"
              ],
              "help": "Units used for power when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: power",
              "signature": null,
              "switch": "-unit_power '<str>'",
              "type": "str",
              "value": "mw"
          },
          "resistance": {
              "defvalue": "ohm",
              "example": [
                  "cli: -unit_resistance 'ohm'",
                  "api: chip.set('unit','resistance',ohm)"
              ],
              "help": "Units used for resistance when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: resistance",
              "signature": null,
              "switch": "-unit_resistance '<str>'",
              "type": "str",
              "value": "ohm"
          },
          "time": {
              "defvalue": "ns",
              "example": [
                  "cli: -unit_time 'ns'",
                  "api: chip.set('unit','time',ns)"
              ],
              "help": "Units used for time when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: time",
              "signature": null,
              "switch": "-unit_time '<str>'",
              "type": "str",
              "value": "ns"
          },
          "voltage": {
              "defvalue": "mv",
              "example": [
                  "cli: -unit_voltage 'mv'",
                  "api: chip.set('unit','voltage',mv)"
              ],
              "help": "Units used for voltage when not explicitly specified.",
              "lock": "false",
              "notes": null,
              "require": null,
              "scope": "job",
              "shorthelp": "Unit: voltage",
              "signature": null,
              "switch": "-unit_voltage '<str>'",
              "type": "str",
              "value": "mv"
          }
      }
  }
  
files: tests/core/data/defaults.json