10. Tools

SiliconCompiler execution depends on implementing adapter code “drivers” for each tool called in a flowgraph. Tools are referenced as named modules by the flowgraph and searched using the find_files() method. A complete set of supported tools can be found in the Tools directory. The table below shows the function interfaces supported in setting up tools.





Used by



Configures tool






Resolves paths at runtime






Returns executable version






Returns executable version

tool version

normalized version




Pre-executable logic






Post-executable logic






Doc generator






Pure Python tool


exit code



For a complete example of a tool setup module, see OpenROAD. For more in depth information about the various ['tool', ...] parameters, see the Schema section of the reference manual.

10.1. setup(chip)

Tool setup is done for each step and index within the run() function prior to launching each individual task. Tools can be configured independently for different steps (ie. the place step is different from the route step), so we need a method for passing information about the current step and index to the setup function. This is accomplished with the reserved ‘scratch’ parameters shown below.

step = chip.get('arg','step')
index = chip.get('arg','index')

All tools are required to bind the tool name to an executable name and to define any required command line options.

chip.set('tool', <toolname>, 'exe', <exename>)
chip.set('tool', <toolname>, 'option', step, index, <option>)

For tools such as TCL based EDA tools, we also need to define the entry script and any associated script directories.

chip.set('tool', <toolname>, 'script', step, index, <entry_script>)
chip.set('tool', <toolname>, 'refdir', step, index, <scriptdir>)
chip.set('tool', <toolname>, 'format', step, index, <scripformat>)

To leverage the run() function’s internal setup checking logic, it is highly recommend to define the parameter requirements, required inputs, expected output, version switch, and supported version numbers using the commands below:

chip.set('tool', <toolname>, 'input', step, index, <list>)
chip.set('tool', <toolname>, 'output', step, index, <list>)
chip.set('tool', <toolname>, 'require' step, index, <list>)
chip.set('tool', <toolname>, 'version' step, index, <list>)
chip.set('tool', <toolname>, 'vswitch', step, index, "<string>")
chip.set('tool', <toolname>, 'report', step, index, "<list>")

10.2. parse_version(stdout)

The run() function includes built in executable version checking, which can be disabled with the ['option', 'novercheck'] parameter. The executable option to use for printing out the version number is specified with the ['tool', <tool>, 'vswitch'] parameter within the setup() function. Commonly used options include ‘-v’, ‘--version’, ‘-version’. The executable output varies widely, so we need a parsing function that processes the output and returns a single uniform version string. The example shows how this function is implemented for the Yosys tool.

def parse_version(stdout):
    # Yosys 0.9+3672 (git sha1 014c7e26, gcc 7.5.0-3ubuntu1~18.04 -fPIC -Os)
    return stdout.split()[1]

The run() function compares the returned parsed version against the ['tool', <tool>, 'version'] parameter specified in the setup() function to ensure that a qualified executable version is being used.

10.3. normalize_version(version)

SC’s version checking logic is based on Python’s PEP-440 standard. In order to perform version checking for tools that do not natively provide PEP-440 compatible version numbers, this function must be implemented to convert the tool-specific versions to a PEP-440 compatible equivalent.

Note that a raw version number may parse as a valid PEP-440 version but not be semantically correct. normalize_version() must be implemented in these cases to ensure version comparisons make sense. For example, we have to do this for Yosys.

def normalize_version(version):
    # Replace '+', which represents a "local version label", with '-', which is
    # an "implicit post release number".
    return version.replace('+', '-')

10.4. pre_process(chip)

For certain tools and steps, we may need to set some Schema parameters immediately before task execution. For example, we may want to set the die and core area before the floorplan step based on the area result from the synthesis step.

10.5. post_process(chip)

The post process step is required to extract metrics from the tool log files. At a minimum the post process step should extract the number of warnings and errors from the tool log file and insert the value into the Schema. The post_process() logic is straight forward, but the regular expression logic can get involved for complex log files. Perhaps some day, EDA tools will produce SiliconCompiler compatible JSON metrics files.

The post_process function can also be used to post process the output data in the case of command line executable to produce an output that can be ingested by the SiliconCompiler framework. The Surelog post_process() implementation illustrates the power of the post_process functionality.

def post_process(chip):
  ''' Tool specific function to run after step execution
  design = chip.top()
  step = chip.get('arg', 'step')

  if step != 'import':
      return 0

  # Look in slpp_all/file_elab.lst for list of Verilog files included in
  # design, read these and concatenate them into one pickled output file.
  with open('slpp_all/file_elab.lst', 'r') as filelist, \
          open(f'outputs/{design}.v', 'w') as outfile:
      for path in filelist.read().split('\n'):
          if not path:
              # skip empty lines
          with open(path, 'r') as infile:
          # in case end of file is missing a newline

10.6. runtime_options(chip)

The distributed execution model of SiliconCompiler mandates that absolute paths be resolved at task run time. The setup() function is run at run() launch to check flow validity, so we need a second function interface (runtime_options) to create the final commandline options. The runtime_options() function inspects the Schema and returns a cmdlist to be used by the ‘exe’ during task execution. The sequence of items used to generate the final command line invocation is as follows:

<'tool',...,'exe'> <'tool',...,'option'> <'tool',...,'script'> <runtime_options()>

The Surelog example below illustrates the process of defining a runtime_options function.

def runtime_options(chip):

  ''' Custom runtime options, returns list of command line options.

  step = chip.get('arg','step')
  index = chip.get('arg','index')

  cmdlist = []

  # source files
  for value in chip.find_files('option', 'ydir'):
      cmdlist.append('-y ' + value)
  for value in chip.find_files('option', 'vlib'):
      cmdlist.append('-v ' + value)
  for value in chip.find_files('option', 'idir'):
      cmdlist.append('-I' + value)
  for value in chip.get('option', 'define'):
      cmdlist.append('-D' + value)
  for value in chip.find_files('option', 'cmdfile'):
      cmdlist.append('-f ' + value)
  for value in chip.find_files('option', 'source'):

  cmdlist.append('-top ' + chip.top())
  # make sure we can find .sv files in ydirs

  # Set up user-provided parameters to ensure we elaborate the correct modules
  for param in chip.getkeys('option', 'param'):
      value = chip.get('option', 'param', param)

  return cmdlist

10.7. make_docs()

The SiliconCompiler includes automated document generators that search all tool modules for functions called make_docs(). It is highly recommended for all tools to include a make_docs() function. The function docstring is used for general narrative, while the body of the function is used to auto-generate a settings table based on the manifest created. At a minimum, the docstring should include a short description and links to the Documentation, Sources, and Installation. The example below shows the make_docs function for surelog.

def make_docs():
  Surelog is a SystemVerilog pre-processor, parser, elaborator,
  and UHDM compiler that provides IEEE design and testbench
  C/C++ VPI and a Python AST API.

  Documentation: https://github.com/chipsalliance/Surelog

  Sources: https://github.com/chipsalliance/Surelog

  Installation: https://github.com/chipsalliance/Surelog


  chip = siliconcompiler.Chip('<design>')
  return chip

10.8. run(chip)

SiliconCompiler supports pure-Python tools that execute a Python function rather than an executable. To define a pure-Python tool, add a function called run() in your tool driver, which takes in a Chip object and implements your tool’s desired functionality. This function should return an integer exit code, with zero indicating success.

Note that pure-Python tool drivers still require a setup() function, but most ['tool', ...] fields will not be meaningful. At the moment, pure-Python tools do not support the following features:

  • Version checking

  • Replay scripts

  • Task timeout

  • Memory usage tracking

  • Breakpoints

  • Output redirection/regex-based logfile parsing

10.9. TCL interface


SiliconCompiler configuration settings are communicated to all script based tools as TCL nested dictionaries.

Schema configuration handoff from SiliconCompiler to script based tools is accomplished within the run() function by using the write_manifest() function to write out the complete schema as a nested TCL dictionary. A snippet of the resulting TCL dictionary is shown below.

dict set sc_cfg asic logiclib [list  NangateOpenCellLibrary ]
dict set sc_cfg asic maxfanout [list  64 ]
dict set sc_cfg design [list  gcd ]
dict set sc_cfg constraint [list gcd.sdc ]
dict set sc_cfg source [list gcd.v ]

This generated manifest also includes a helper function, sc_top, that handles the logic for determining the name of the design’s top-level module (mirroring the logic of top()).

It is the responsibility of the tool reference flow developer to bind the standardized SiliconCompiler TCL schema to the tool specific TCL commands and variables. The TCL snippet below shows how the OpenRoad TCL reference flow remaps the TCL nested dictionary to simple lists and scalars at the beginning of the flow for the sake of clarity.

set sc_design     [sc_top]
set sc_optmode    [dict get $sc_cfg optmode]

# APR Parameters
set sc_mainlib     [lindex [dict get $sc_cfg asic logiclib] 0]
set sc_stackup     [dict get $sc_cfg option stackup]
set sc_targetlibs  [dict get $sc_cfg asic logiclib]
set sc_density     [dict get $sc_cfg asic density]
set sc_hpinlayer   [dict get $sc_cfg asic hpinlayer]
set sc_vpinlayer   [dict get $sc_cfg asic vpinlayer]
set sc_pdk         [dict get $sc_cfg option pdk]
set sc_hpinmetal   [dict get $sc_cfg pdk $sc_pdk grid $sc_stackup $sc_hpinlayer name]
set sc_vpinmetal   [dict get $sc_cfg pdk $sc_pdk grid $sc_stackup $sc_vpinlayer name]