files: # Copyright 2020 Silicon Compiler Authors. All Rights Reserved.
  
  import base64
  import glob
  import math
  import multiprocessing
  import importlib
  import json
  import os
  import requests
  import shutil
  import subprocess
  import sys
  import time
  import urllib.parse
  import uuid
  
  from siliconcompiler.crypto import *
  from siliconcompiler import utils
  
  ###################################
  def get_base_url(chip):
      '''Helper method to get the root URL for API calls, given a Chip object.
      '''
  
      rcfg = chip.status['remote_cfg']
      remote_host = rcfg['address']
      if 'port' in rcfg:
          remote_port = rcfg['port']
      else:
          remote_port = 443
      remote_host += ':' + str(remote_port)
      if remote_host.startswith('http'):
          remote_protocol = ''
      else:
          remote_protocol = 'https://' if str(remote_port) == '443' else 'http://'
      return remote_protocol + remote_host
  
  ###################################
  def remote_preprocess(chip):
      '''Helper method to run a local import stage for remote jobs.
      '''
  
      # Assign a new 'job_hash' to the chip if necessary.
      if not 'jobhash' in chip.status:
          job_hash = uuid.uuid4().hex
          chip.status['jobhash'] = job_hash
  
      # Setup up tools for all local functions
      flow = chip.get('option', 'flow')
      remote_steplist = chip.getkeys('flowgraph', flow)
      local_step = remote_steplist[0]
      indexlist = chip.getkeys('flowgraph', flow, local_step)
      for index in indexlist:
          tool = chip.get('flowgraph', flow, local_step, index, 'tool')
          # Setting up tool is optional (step may be a builtin function)
          if tool and tool not in chip.builtin:
              chip._setup_tool(tool, local_step, index)
  
          # Need to override steplist here to make sure check_manifest() doesn't
          # check steps that haven't been setup.
          chip.set('option', 'steplist', local_step)
  
          # Run the actual import step locally.
          # We can pass in an empty 'status' dictionary, since _runtask() will
          # only look up a step's depedencies in this dictionary, and the first
          # step should have none.
          chip._runtask(local_step, index, {})
  
      # Set 'steplist' to only the remote steps, for the future server-side run.
      remote_steplist = remote_steplist[1:]
      chip.set('option', 'steplist', remote_steplist, clobber=True)
  
  ###################################
  def remote_run(chip):
      '''Helper method to run a job stage on a remote compute cluster.
      Note that files will not be copied to the remote stage; typically
      the source files will be copied into the cluster's storage before
      calling this method.
      If the "-remote" parameter was not passed in, this method
      will print a warning and do nothing.
      This method assumes that the given stage should not be skipped,
      because it is called from within the `Chip.run(...)` method.
  
      '''
  
      # Time how long the process has been running for.
      step_start = time.monotonic()
  
      # Ask the remote server to start processing the requested step.
      request_remote_run(chip)
  
      # Remove the local 'import.tar.gz' archive.
      local_archive = os.path.join(chip.get('option', 'builddir'),
                                   chip.get('design'),
                                   chip.get('option', 'jobname'),
                                   'import.tar.gz')
      if os.path.isfile(local_archive):
          os.remove(local_archive)
  
      # Check the job's progress periodically until it finishes.
      is_busy = True
      while is_busy:
        chip.logger.info("Job is still running. (%d seconds)"%(
                         int(time.monotonic() - step_start)))
        time.sleep(30)
        try:
            is_busy = is_job_busy(chip)
        except:
            # Sometimes an exception is raised if the request library cannot
            # reach the server due to a transient network issue.
            # Retrying ensures that jobs don't break off when the connection drops.
            is_busy = True
            chip.logger.info("Unknown network error encountered: retrying.")
      chip.logger.info("Remote job run completed!")
  
  ###################################
  def request_remote_run(chip):
      '''Helper method to make a web request to start a job stage.
      '''
  
      # Set the request URL.
      remote_run_url = urllib.parse.urljoin(get_base_url(chip), '/remote_run/')
  
      # Use authentication if necessary.
      job_nameid = f"{chip.get('option', 'jobname')}"
      post_params = {
          'chip_cfg': chip.cfg,
          'params': {
              'job_hash': chip.status['jobhash'],
          }
      }
      local_build_dir = os.path.join(chip.get('option', 'builddir'),
                                     chip.get('design'),
                                     job_nameid)
      rcfg = chip.status['remote_cfg']
      if ('username' in rcfg) and ('password' in rcfg):
          post_params['params']['username'] = rcfg['username']
          post_params['params']['key'] = rcfg['password']
  
      # If '-remote_user' and '-remote_key' are not both specified,
      # no authorizaion is configured; proceed without crypto.
      # If they were specified, these files are now encrypted.
      subprocess.run(['tar',
                      '-czf',
                      'import.tar.gz',
                      '--exclude',
                      'import.tar.gz',
                      '.'],
                     stdout=subprocess.PIPE,
                     stderr=subprocess.STDOUT,
                     cwd=local_build_dir)
      upload_file = os.path.abspath(os.path.join(local_build_dir, 'import.tar.gz'))
  
      # Make the actual request, streaming the bulk data as a multipart file.
      # Redirected POST requests are translated to GETs. This is actually
      # part of the HTTP spec, so we need to manually follow the trail.
      redirect_url = remote_run_url
      while redirect_url:
          with open(upload_file, 'rb') as f:
              resp = requests.post(redirect_url,
                                   files={'import': f,
                                          'params': json.dumps(post_params)},
                                   allow_redirects=False)
              if resp.status_code == 302:
                  redirect_url = resp.headers['Location']
              elif resp.status_code >= 400:
                  chip.logger.error(resp.json()['message'])
                  chip.logger.error('Error starting remote job run; quitting.')
                  raise RuntimeError('Remote server returned unrecoverable error code.')
              else:
                  chip.logger.info(resp.text)
                  return
  
  ###################################
  def is_job_busy(chip):
      '''Helper method to make an async request asking the remote server
      whether a job is busy, or ready to accept a new step.
      Returns True if the job is busy, False if not.
      '''
  
      # Set the request URL.
      remote_run_url = urllib.parse.urljoin(get_base_url(chip), '/check_progress/')
  
      # Set common parameters.
      post_params = {
          'job_hash': chip.status['jobhash'],
          'job_id': chip.get('option', 'jobname'),
      }
  
      # Set authentication parameters if necessary.
      rcfg = chip.status['remote_cfg']
      if ('username' in rcfg) and ('password' in rcfg):
          post_params['username'] = rcfg['username']
          post_params['key'] = rcfg['password']
  
      # Make the request and print its response.
      redirect_url = remote_run_url
      while redirect_url:
          resp = requests.post(redirect_url,
                               data=json.dumps(post_params),
                               allow_redirects=False)
          if resp.status_code == 302:
              redirect_url = resp.headers['Location']
          else:
              return (resp.text != "Job has no running steps.")
  
  ###################################
  def delete_job(chip):
      '''Helper method to delete a job from shared remote storage.
      '''
  
      # Set the request URL.
      remote_run_url = urllib.parse.urljoin(get_base_url(chip), '/delete_job/')
  
      # Set common parameter.
      post_params = {
          'job_hash': chip.status['jobhash'],
      }
  
      # Set authentication parameters if necessary.
      rcfg = chip.status['remote_cfg']
      if ('username' in rcfg) and ('password' in rcfg):
          post_params['username'] = rcfg['username']
          post_params['key'] = rcfg['password']
  
      # Make the request.
      redirect_url = remote_run_url
      while redirect_url:
          resp = requests.post(redirect_url,
                               data=json.dumps(post_params),
                               allow_redirects=False)
          if resp.status_code == 302:
                  redirect_url = resp.headers['Location']
          else:
              response = resp.text
              return response
  
  ###################################
  def fetch_results_request(chip):
      '''Helper method
  
  [... the rest is omitted ...]
files: siliconcompiler/client.py
files: # Copyright 2020 Silicon Compiler Authors. All Rights Reserved.
  
  import base64
  import glob
  import math
  import multiprocessing
  import importlib
  import json
  import os
  import requests
  import shutil
  import subprocess
  import sys
  import time
  import urllib.parse
  import uuid
  
  from siliconcompiler.crypto import *
  from siliconcompiler import utils
  
  ###################################
  def get_base_url(chip):
      '''Helper method to get the root URL for API calls, given a Chip object.
      '''
  
      rcfg = chip.status['remote_cfg']
      remote_host = rcfg['address']
      if 'port' in rcfg:
          remote_port = rcfg['port']
      else:
          remote_port = 443
      remote_host += ':' + str(remote_port)
      if remote_host.startswith('http'):
          remote_protocol = ''
      else:
          remote_protocol = 'https://' if str(remote_port) == '443' else 'http://'
      return remote_protocol + remote_host
  
  ###################################
  def remote_preprocess(chip):
      '''Helper method to run a local import stage for remote jobs.
      '''
  
      # Assign a new 'job_hash' to the chip if necessary.
      if not 'jobhash' in chip.status:
          job_hash = uuid.uuid4().hex
          chip.status['jobhash'] = job_hash
  
      # Setup up tools for all local functions
      flow = chip.get('option', 'flow')
      remote_steplist = chip.getkeys('flowgraph', flow)
      local_step = remote_steplist[0]
      indexlist = chip.getkeys('flowgraph', flow, local_step)
      for index in indexlist:
          tool = chip.get('flowgraph', flow, local_step, index, 'tool')
          # Setting up tool is optional (step may be a builtin function)
          if tool:
              chip.set('arg', 'step', local_step)
              chip.set('arg', 'index', index)
              func = chip.find_function(tool, 'setup', 'tools')
              func(chip)
  
          # Need to override steplist here to make sure check_manifest() doesn't
          # check steps that haven't been setup.
          chip.set('option', 'steplist', local_step)
  
          # Run the actual import step locally.
          # We can pass in an empty 'status' dictionary, since _runtask() will
          # only look up a step's depedencies in this dictionary, and the first
          # step should have none.
          chip._runtask(local_step, index, {})
  
      # Set 'steplist' to only the remote steps, for the future server-side run.
      remote_steplist = remote_steplist[1:]
      chip.set('option', 'steplist', remote_steplist, clobber=True)
  
  ###################################
  def remote_run(chip):
      '''Helper method to run a job stage on a remote compute cluster.
      Note that files will not be copied to the remote stage; typically
      the source files will be copied into the cluster's storage before
      calling this method.
      If the "-remote" parameter was not passed in, this method
      will print a warning and do nothing.
      This method assumes that the given stage should not be skipped,
      because it is called from within the `Chip.run(...)` method.
  
      '''
  
      # Time how long the process has been running for.
      step_start = time.monotonic()
  
      # Ask the remote server to start processing the requested step.
      request_remote_run(chip)
  
      # Remove the local 'import.tar.gz' archive.
      local_archive = os.path.join(chip.get('option', 'builddir'),
                                   chip.get('design'),
                                   chip.get('option', 'jobname'),
                                   'import.tar.gz')
      if os.path.isfile(local_archive):
          os.remove(local_archive)
  
      # Check the job's progress periodically until it finishes.
      is_busy = True
      while is_busy:
        chip.logger.info("Job is still running. (%d seconds)"%(
                         int(time.monotonic() - step_start)))
        time.sleep(30)
        try:
            is_busy = is_job_busy(chip)
        except:
            # Sometimes an exception is raised if the request library cannot
            # reach the server due to a transient network issue.
            # Retrying ensures that jobs don't break off when the connection drops.
            is_busy = True
            chip.logger.info("Unknown network error encountered: retrying.")
      chip.logger.info("Remote job run completed!")
  
  ###################################
  def request_remote_run(chip):
      '''Helper method to make a web request to start a job stage.
      '''
  
      # Set the request URL.
      remote_run_url = urllib.parse.urljoin(get_base_url(chip), '/remote_run/')
  
      # Use authentication if necessary.
      job_nameid = f"{chip.get('option', 'jobname')}"
      post_params = {
          'chip_cfg': chip.cfg,
          'params': {
              'job_hash': chip.status['jobhash'],
          }
      }
      local_build_dir = os.path.join(chip.get('option', 'builddir'),
                                     chip.get('design'),
                                     job_nameid)
      rcfg = chip.status['remote_cfg']
      if ('username' in rcfg) and ('password' in rcfg):
          post_params['params']['username'] = rcfg['username']
          post_params['params']['key'] = rcfg['password']
  
      # If '-remote_user' and '-remote_key' are not both specified,
      # no authorizaion is configured; proceed without crypto.
      # If they were specified, these files are now encrypted.
      subprocess.run(['tar',
                      '-czf',
                      'import.tar.gz',
                      '--exclude',
                      'import.tar.gz',
                      '.'],
                     stdout=subprocess.PIPE,
                     stderr=subprocess.STDOUT,
                     cwd=local_build_dir)
      upload_file = os.path.abspath(os.path.join(local_build_dir, 'import.tar.gz'))
  
      # Make the actual request, streaming the bulk data as a multipart file.
      # Redirected POST requests are translated to GETs. This is actually
      # part of the HTTP spec, so we need to manually follow the trail.
      redirect_url = remote_run_url
      while redirect_url:
          with open(upload_file, 'rb') as f:
              resp = requests.post(redirect_url,
                                   files={'import': f,
                                          'params': json.dumps(post_params)},
                                   allow_redirects=False)
              if resp.status_code == 302:
                  redirect_url = resp.headers['Location']
              elif resp.status_code >= 400:
                  chip.logger.error(resp.json()['message'])
                  chip.logger.error('Error starting remote job run; quitting.')
                  raise RuntimeError('Remote server returned unrecoverable error code.')
              else:
                  chip.logger.info(resp.text)
                  return
  
  ###################################
  def is_job_busy(chip):
      '''Helper method to make an async request asking the remote server
      whether a job is busy, or ready to accept a new step.
      Returns True if the job is busy, False if not.
      '''
  
      # Set the request URL.
      remote_run_url = urllib.parse.urljoin(get_base_url(chip), '/check_progress/')
  
      # Set common parameters.
      post_params = {
          'job_hash': chip.status['jobhash'],
          'job_id': chip.get('option', 'jobname'),
      }
  
      # Set authentication parameters if necessary.
      rcfg = chip.status['remote_cfg']
      if ('username' in rcfg) and ('password' in rcfg):
          post_params['username'] = rcfg['username']
          post_params['key'] = rcfg['password']
  
      # Make the request and print its response.
      redirect_url = remote_run_url
      while redirect_url:
          resp = requests.post(redirect_url,
                               data=json.dumps(post_params),
                               allow_redirects=False)
          if resp.status_code == 302:
              redirect_url = resp.headers['Location']
          else:
              return (resp.text != "Job has no running steps.")
  
  ###################################
  def delete_job(chip):
      '''Helper method to delete a job from shared remote storage.
      '''
  
      # Set the request URL.
      remote_run_url = urllib.parse.urljoin(get_base_url(chip), '/delete_job/')
  
      # Set common parameter.
      post_params = {
          'job_hash': chip.status['jobhash'],
      }
  
      # Set authentication parameters if necessary.
      rcfg = chip.status['remote_cfg']
      if ('username' in rcfg) and ('password' in rcfg):
          post_params['username'] = rcfg['username']
          post_params['key'] = rcfg['password']
  
      # Make the request.
      redirect_url = remote_run_url
      while redirect_url:
          resp = requests.post(redirect_url,
                               data=json.dumps(post_params),
                               allow_redirects=False)
          if resp.status_code == 302:
                  redirect_url = resp.headers['Location']
          else:
              response
  
  [... the rest is omitted ...]
files: siliconcompiler/client.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
  
  [... the rest is omitted ...]
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
  
  [... the rest is omitted ...]
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
  
  [... the rest is omitted ...]
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
  
  [... the rest is omitted ...]
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>'
  
  [... the rest is omitted ...]
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>'
  
  [... the rest is omitted ...]
files: tests/core/data/defaults.json