OpenROAD
OpenROAD

Welcome to OpenROAD’s documentation!

The OpenROAD (“Foundations and Realization of Open, Accessible Design”) project was launched in June 2018 within the DARPA IDEA program. OpenROAD aims to bring down the barriers of cost, expertise and unpredictability that currently block designers’ access to hardware implementation in advanced technologies. The project team (Qualcomm, Arm and multiple universities and partners, led by UC San Diego) is developing a fully autonomous, open-source tool chain for digital layout generation across die, package and board, with initial focus on the RTL-to-GDSII phase of system-on-chip design. Thus, OpenROAD holistically attacks the multiple facets of today’s design cost crisis: engineering resources, design tool licenses, project schedule, and risk.

The IDEA program targets no-human-in-loop (NHIL) design, with 24-hour turnaround time and zero loss of power-performance-area (PPA) design quality.

The NHIL target requires tools to adapt and auto-tune successfully to flow completion without or with significantly minimal human intervention. Machine intelligence augments human expertise through efficient modeling and prediction of flow outcomes during layout generation.

24 hours runtime target implies that problems must be strategically decomposed into optimal partitions during the design process through intellgient distribution and management of computational resources. This ensures that the design constraints are met for schedule, performance and cost. Any quality loss due to decomposition that uses a parallel and distributed search over cloud resources, is subsequently recovered through improved flow predictability and enhanced optimization.

For a technical description of the OpenROAD flow, please refer to our DAC paper: Toward an Open-Source Digital Flow: First Learnings from the OpenROAD Project. Also, available from ACM Digital Library.

How to navigate this documentation

  • If you are a user, start with the Getting Started guide, and then move on to the User Guide.
  • If you are willing to contribute, see the Getting Involved section.
  • If you are a developer with EDA background, learn more about how you can use OpenROAD as the infrastructure for your tools in the Developer Guide section.

See FAQs and Capabilities/Limitations for relevant background on the project.

How to get in touch

We maintain the following channels for communication:

Site Map

Getting Started

OpenROAD is divided into a number of tools that are orchestrated together to achieve RTL-to-GDS. As of the current implementation, the flow is divided into two stages:

  1. Logic Synthesis: is performed by yosys.
  2. Floorplanning through Detailed Routing: are performed by OpenROAD App.

In order to integrate the flow steps, OpenROAD-flow-scripts repository includes the necessary scripts to build and test the flow.

Prerequisites

Before proceeding to the next step: 1. Install Docker on your machine, OR 2. Check that build dependencies for all tools are installed on your machine.

During initial Setup or if you have installed on a new machine, run this script: run ./etc/DependencyInstaller.sh

Get the tools

There are currently two options to get OpenROAD tools.

Option 1: build from sources using Docker
Clone and Build
$ git clone --recursive https://github.com/The-OpenROAD-Project/OpenROAD-flow-scripts
$ cd OpenROAD-flow-scripts
$ ./build_openroad.sh
Verify Installation

The binaries should be available on your $PATH after setting up the environment.

$ docker run -it -u $(id -u ${USER}):$(id -g ${USER}) -v $(pwd)/flow/platforms:/OpenROAD-flow-scripts/flow/platforms:ro openroad/flow-scripts
[inside docker] $ source ./setup_env.sh
[inside docker] $ yosys -help
[inside docker] $ openroad -help
[inside docker] $ cd flow
[inside docker] $ make
[inside docker] $ exit
Option 2: Build from sources locally
Clone and Build
$ git clone --recursive https://github.com/The-OpenROAD-Project/OpenROAD-flow-scripts
$ cd OpenROAD-flow-scripts
$ ./build_openroad.sh --local
Verify Installation

The binaries should be available on your $PATH after setting up the environment.

$ source ./setup_env.sh
$ yosys -help
$ openroad -help
$ exit

Designs

Sample design configurations are available in the designs directory. You can select a design using either of the following methods:

1. The flow Makefile contains a list of sample design configurations at the top of the file. Uncomment the respective line to select the design 2. Specify the design using the shell environment, e.g. make DESIGN_CONFIG=./designs/nangate45/swerv/config.mk or export DESIGN_CONFIG=./designs/nangate45/swerv/config.mk ; make

By default, the simple design gcd is selected. We recommend implementing this design first to validate your flow and tool setup.

Adding a New Design

To add a new design, we recommend looking at the included designs for examples of how to set one up.

Platforms

OpenROAD-flow-scripts supports Verilog to GDS for the following open platforms: Nangate45 / FreePDK45

These platforms have a permissive license which allows us to redistribute the PDK and OpenROAD platform-specific files. The platform files and license(s) are located in platforms/{platform}.

OpenROAD-flow-scripts also supports the following commercial platforms: TSMC65LP / GF14 (in progress)

The PDKs and platform-specific files for these kits cannot be provided due to NDA restrictions. However, if you are able to access these platforms, you can create the necessary platform-specific files yourself.

Once the platform is setup. Create a new design configuration with information about the design. See sample configurations in the design directory.

Adding a New Platform

At this time, we recommend looking at the Nangate45 as an example of how to set up a new platform for OpenROAD-flow-scripts.

Implement the Design

Run make to perform Verilog to GDS. The final output will be located at flow/results/{platform}/{design_name}/6_final.gds

Miscellaneous

tiny-tests - easy to add, single concern, single Verilog file

The tiny-tests are have been designed with two design goals in mind:

  1. It should be trivial to add a new test: simply add a tiny standalone Verilog file to OpenROAD-flow-scripts/flow/designs/src/tiny-tests
  2. Each test should be as small and as standalone as possible and be a single concern test.

To run a test:

make DESIGN_NAME=SmallPinCount DESIGN_CONFIG=`pwd`/designs/tiny-tests.mk
nangate45 smoke-test harness for top level Verilog designs
  1. Drop your Verilog files into designs/src/harness
  2. Start the workflow:
make DESIGN_NAME=TopLevelName DESIGN_CONFIG=`pwd`/designs/harness.mk

Note

TIP! Start with a small tiny submodule in your design with few pins

User Guide

OpenROAD is divided into a number of tools that are orchestrated together to achieve RTL-to-GDS. As of the current implementation, the flow is divided into three stages:

  1. Logic Synthesis: is performed by yosys.
  2. Floorplanning through Detailed Routing: are performed by OpenROAD App.
  3. KLayout: GDS merge, DRC and LVS (public PDKs)

To Run OpenROAD flow, we provide scripts to automate the RTL-to-GDS stages. Alternatively, you can run the individual steps manually.

GitHub: OpenROAD-flow-scripts

Code Organization

This repository serves as an example RTL-to-GDS flow using the OpenROAD tools.

The two main components are:

  1. tools: This directory contains the source code for the entire openroad app (via submodules) as well as other tools required for the flow. The script build_openroad.sh in this repository will automatically build the OpenROAD toolchain.
  2. flow: This directory contains reference recipes and scripts to run | designs through the flow. It also contains platforms and test designs.

Setup

The flow has the following dependencies:

  • OpenROAD
  • KLayout
  • Yosys

The dependencies can either be obtained from a pre-compiled build export or built manually. See the KLayout website for installation instructions.

Using the flow

See the flow README for details about the flow and how to run designs through the flow.

Logic Synthesis

GitHub: https://github.com/The-OpenROAD-Project/yosys

Setup

Requirements

  • C++ compiler with C++11 support (up-to-date CLANG or GCC is recommended)
  • GNU Flex, GNU Bison, and GNU Make.
  • TCL, readline and libffi.

On Ubuntu:

$ sudo apt-get install build-essential clang bison flex \
        libreadline-dev gawk tcl-dev libffi-dev git \
        graphviz xdot pkg-config python3 libboost-system-dev \
        libboost-python-dev libboost-filesystem-dev zlib1g-dev

On Mac OS X Homebrew can be used to install dependencies (from within cloned yosys repository):

$ brew tap Homebrew/bundle && brew bundle

To configure the build system to use a specific compiler, use one of

$ make config-clang
$ make config-gcc

Build

To build Yosys simply type ‘make’ in this directory.

$ make
$ sudo make install

Synthesis Script

yosys -import

if {[info exist ::env(DC_NETLIST)]} {
exec cp $::env(DC_NETLIST) $::env(RESULTS_DIR)/1_1_yosys.v
exit
}

# Don't change these unless you know what you are doing
set stat_ext    "_stat.rep"
set gl_ext      "_gl.v"
set abc_script  "+read_constr,$::env(SDC_FILE);strash;ifraig;retime,-D,{D},-M,6;strash;dch,-f;map,-p-M,1,{D},-f;topo;dnsize;buffer,-p;upsize;"


# Setup verilog include directories
set vIdirsArgs ""
if {[info exist ::env(VERILOG_INCLUDE_DIRS)]} {
    foreach dir $::env(VERILOG_INCLUDE_DIRS) {
        lappend vIdirsArgs "-I$dir"
    }
    set vIdirsArgs [join $vIdirsArgs]
}


# read verilog files
foreach file $::env(VERILOG_FILES) {
    read_verilog -sv {*}$vIdirsArgs $file
}


# Read blackbox stubs of standard cells. This allows for standard cell (or
# structural netlist) support in the input verilog
read_verilog $::env(BLACKBOX_V_FILE)

# Apply toplevel parameters (if exist)
if {[info exist ::env(VERILOG_TOP_PARAMS)]} {
    dict for {key value} $::env(VERILOG_TOP_PARAMS) {
        chparam -set $key $value $::env(DESIGN_NAME)
    }
}

# Read platform specific mapfile for OPENROAD_CLKGATE cells
if {[info exist ::env(CLKGATE_MAP_FILE)]} {
    read_verilog $::env(CLKGATE_MAP_FILE)
}

# Use hierarchy to automatically generate blackboxes for known memory macro.
# Pins are enumerated for proper mapping
if {[info exist ::env(BLACKBOX_MAP_TCL)]} {
    source $::env(BLACKBOX_MAP_TCL)
}

# generic synthesis
synth  -top $::env(DESIGN_NAME) -flatten

# Optimize the design
opt -purge

# technology mapping of latches
if {[info exist ::env(LATCH_MAP_FILE)]} {
    techmap -map $::env(LATCH_MAP_FILE)
}

# technology mapping of flip-flops
dfflibmap -liberty $::env(OBJECTS_DIR)/merged.lib
opt

# Technology mapping for cells
abc -D [expr $::env(CLOCK_PERIOD) * 1000] \
    -constr "$::env(SDC_FILE)" \
    -liberty $::env(OBJECTS_DIR)/merged.lib \
    -script $abc_script \
    -showtmp

# technology mapping of constant hi- and/or lo-drivers
hilomap -singleton \
        -hicell {*}$::env(TIEHI_CELL_AND_PORT) \
        -locell {*}$::env(TIELO_CELL_AND_PORT)

# replace undef values with defined constants
setundef -zero

# Splitting nets resolves unwanted compound assign statements in netlist (assign {..} = {..})
splitnets

# insert buffer cells for pass through wires
insbuf -buf {*}$::env(MIN_BUF_CELL_AND_PORTS)

# remove unused cells and wires
opt_clean -purge

# reports
tee -o $::env(REPORTS_DIR)/synth_check.txt check
tee -o $::env(REPORTS_DIR)/synth_stat.txt stat -liberty $::env(OBJECTS_DIR)/merged.lib

# write synthesized design
write_verilog -noattr -noexpr -nohex -nodec $::env(RESULTS_DIR)/1_1_yosys.v
Initialize Floorplan
initialize_floorplan
[-site site_name]          LEF site name for ROWS
[-tracks tracks_file]      routing track specification
-die_area "lx ly ux uy"    die area in microns
[-core_area "lx ly ux uy"] core area in microns
or
-utilization util          utilization (0-100 percent)
[-aspect_ratio ratio]      height / width, default 1.0
[-core_space space]        space around core, default 0.0 (microns)

The die area and core size used to write ROWs can be specified explicitly with the -die_area and -core_area arguments. Alternatively, the die and core area can be computed from the design size and utilization as show below:

If no -tracks file is used the routing layers from the LEF are used.

core_area = design_area / (utilization / 100)
core_width = sqrt(core_area / aspect_ratio)
core_height = core_width * aspect_ratio
core = ( core_space, core_space ) ( core_space + core_width, core_space + core_height )
die = ( 0, 0 ) ( core_width + core_space * 2, core_height + core_space * 2 )

Place pins around core boundary.

auto_place_pins pin_layer
Gate Resizer

Gate resizer commands are described below. The resizer commands stop when the design area is -max_utilization util percent of the core area. util is between 0 and 100.

set_wire_rc [-layer layer_name]
            [-resistance res ]
        [-capacitance cap]
        [-corner corner_name]

The set_wire_rc command sets the resistance and capacitance used to estimate delay of routing wires. Use -layer or -resistance and -capacitance. If -layer is used, the LEF technology resistance and area/edge capacitance values for the layer are used. The units for -resistance and -capacitance are from the first liberty file read, resistance_unit/distance_unit and liberty capacitance_unit/distance_unit. RC parasitics are added based on placed component pin locations. If there are no component locations no parasitics are added. The resistance and capacitance are per distance unit of a routing wire. Use the set_units command to check units or set_cmd_units to change units. They should represent “average” routing layer resistance and capacitance. If the set_wire_rc command is not called before resizing, the default_wireload model specified in the first liberty file or with the SDC set_wire_load command is used to make parasitics.

buffer_ports [-inputs]
        [-outputs]
        -buffer_cell buffer_cell

The buffer_ports -inputs command adds a buffer between the input and its loads. The buffer_ports -outputs adds a buffer between the port driver and the output port. If The default behavior is -inputs and -outputs if neither is specified.

resize [-libraries resize_libraries]
    [-dont_use cells]
    [-max_utilization util]

The resize command resizes gates to normalize slews.

The -libraries option specifies which libraries to use when resizing. resize_libraries defaults to all of the liberty libraries that have been read. Some designs have multiple libraries with different transistor thresholds (Vt) and are used to trade off power and speed. Chosing a low Vt library uses more power but results in a faster design after the resizing step. Use the -dont_use option to specify a list of patterns of cells to not use. For example, */DLY* says do not use cells with names that begin with DLY in all libraries.

repair_max_cap -buffer_cell buffer_cell
            [-max_utilization util]
repair_max_slew -buffer_cell buffer_cell
                [-max_utilization util]

The repair_max_cap and repair_max_slew commands repair nets with maximum capacitance or slew violations by inserting buffers in the net.

repair_max_fanout -max_fanout fanout
                -buffer_cell buffer_cell
                [-max_utilization util]

The repair_max_fanout command repairs nets with a fanout greater than fanout by inserting buffers between the driver and the loads. Buffers are located at the center of each group of loads.

repair_tie_fanout [-max_fanout fanout]
                [-verbose]
                lib_port

The repair_tie_fanout command repairs tie high/low nets with fanout greater than fanout by cloning the tie high/low driver. lib_port is the tie high/low port, which can be a library/cell/port name or object returned by get_lib_pins. Clones are located at the center of each group of loads.

repair_hold_violations -buffer_cell buffer_cell
                    [-max_utilization util]

The repair_hold_violations command inserts buffers to repair hold check violations.

report_design_area

The report_design_area command reports the area of the design’s components and the utilization.

report_floating_nets [-verbose]

The report_floating_nets command reports nets with only one pin connection. Use the -verbose flag to see the net names.

A typical resizer command file is shown below.

read_lef nlc18.lef
read_liberty nlc18.lib
read_def mea.def
read_sdc mea.sdc
set_wire_rc -layer metal2
set buffer_cell [get_lib_cell nlc18_worst/snl_bufx4]
set max_util 90
buffer_ports -buffer_cell $buffer_cell
resize -resize
repair_max_cap -buffer_cell $buffer_cell -max_utilization $max_util
repair_max_slew -buffer_cell $buffer_cell -max_utilization $max_util
# repair tie hi/low before max fanout so they don't get buffered
repair_tie_fanout -max_fanout 100 Nangate/LOGIC1_X1/Z
repair_max_fanout -max_fanout 100 -buffer_cell $buffer_cell -max_utilization $max_util
repair_hold_violations -buffer_cell $buffer_cell -max_utilization $max_util

Note that OpenSTA commands can be used to report timing metrics before or after resizing the design.

set_wire_rc -layer metal2
report_checks
report_tns
report_wns
report_checks

resize

report_checks
report_tns
report_wns
Timing Analysis

Timing analysis commands are documented in src/OpenSTA/doc/OpenSTA.pdf.

After the database has been read from LEF/DEF, Verilog or an OpenDB database, use the read_liberty command to read Liberty library files used by the design.

The example script below timing analyzes a database.

read_liberty liberty1.lib
read_db reg1.db
create_clock -name clk -period 10 {clk1 clk2 clk3}
set_input_delay -clock clk 0 {in1 in2}
set_output_delay -clock clk 0 out
report_checks
MacroPlace

TritonMacroPlace https://github.com/The-OpenROAD-Project/TritonMacroPlace

macro_placement -global_config <global_config_file>
  • global_config : Set global config file loction. [string]
Global Config Example
set ::HALO_WIDTH_V 1
set ::HALO_WIDTH_H 1
set ::CHANNEL_WIDTH_V 0
set ::CHANNEL_WIDTH_H 0
  • HALO_WIDTH_V : Set macro’s vertical halo. [float; unit: micron]
  • HALO_WIDTH_H : Set macro’s horizontal halo. [float; unit: micron]
  • CHANNEL_WIDTH_V : Set macro’s vertical channel width. [float; unit: micron]
  • CHANNEL_WIDTH_H : Set macro’s horizontal channel width. [float; unit: micron]
Tapcell

Tapcell and endcap insertion.

tapcell -tapcell_master <tapcell_master>
        -endcap_master <endcap_master>
        -endcap_cpp <endcap_cpp>
        -distance <dist>
        -halo_width_x <halo_x>
        -halo_width_y <halo_y>
        -tap_nwin2_master <tap_nwin2_master>
        -tap_nwin3_master <tap_nwin3_master>
        -tap_nwout2_master <tap_nwout2_master>
        -tap_nwout3_master <tap_nwout3_master>
        -tap_nwintie_master <tap_nwintie_master>
        -tap_nwouttie_master <tap_nwouttie_master>
        -cnrcap_nwin_master <cnrcap_nwin_master>
        -cnrcap_nwout_master <cnrcap_nwout_master>
        -incnrcap_nwin_master <incnrcap_nwin_master>
        -incnrcap_nwout_master <incnrcap_nwout_master>
        -tbtie_cpp <tbtie_cpp>
        -no_cell_at_top_bottom
        -add_boundary_cell

You can find script examples for supported technologies tap/etc/scripts

Global Placement

RePlAce global placement. https://github.com/The-OpenROAD-Project/RePlAce

global_placement -skip_initial_place
                 -incremental
                 -bin_grid_count <grid_count>
                 -density <density>
                 -init_density_penalty <init_density_penalty>
                 -init_wirelength_coef <init_wirelength_coef>
                 -min_phi_coef <min_phi_coef>
                 -max_phi_coef <max_phi_coef>
                 -overflow <overflow>
                 -initial_place_max_iter <max_iter>
                 -initial_place_max_fanout <max_fanout>
                 -verbose_level <level>
Flow Control
  • skip_initial_place : Skip the initial placement (BiCGSTAB solving) before Nesterov placement. IP improves HPWL by ~5% on large designs. Equal to ‘-initial_place_max_iter 0’
  • incremental : Enable the incremental global placement. Users would need to tune other parameters (e.g. init_density_penalty) with pre-placed solutions.
Tuning Parameters
  • bin_grid_count : Set bin grid’s counts. Default: Defined by internal algorithm. [64,128,256,512,…, int]
  • density : Set target density. Default: 0.70 [0-1, float]
  • init_density_penalty : Set initial density penalty. Default: 8e-5 [1e-6 - 1e6, float]
  • __init_wire_length__coef__ : Set initial wirelength coefficient. Default: 0.25 [unlimited, float]
  • min_phi_coef : Set pcof_min(µ_k Lower Bound). Default: 0.95 [0.95-1.05, float]
  • max_phi_coef : Set pcof_max(µ_k Upper Bound). Default: 1.05 [1.00-1.20, float]
  • overflow : Set target overflow for termination condition. Default: 0.1 [0-1, float]
  • initial_place_max_iter : Set maximum iterations in initial place. Default: 20 [0-, int]
  • initial_place_max_fanout : Set net escape condition in initial place when ‘fanout >= initial_place_max_fanout’. Default: 200 [1-, int]
Other Options
  • verbose_level : Set verbose level for RePlAce. Default: 1 [0-10, int]
Detailed Placement

Legalize a design that has been globally placed.

legalize_placement [-constraints constraints_file]
Clock Tree Synthesis

Create clock tree subnets.

clock_tree_synthesis -root_buf <root_buf> \
                     -buf_list <tree_bufs> \
                    [-clk_nets <list_of_clk_nets>]
  • root_buffer is the master cell of the buffer that serves as root
  • buf_list is the list of master cells of the buffers that can be used for building the clock tree.
  • clk_nets is a string containing the names of the clock roots. If this parameter is ommitted, TritonCTS looks for the clock roots automatically.
Global Routing

FastRoute global route. Generate routing guides given a placed design.

fastroute -output_file out_file
        -capacity_adjustment <cap_adjust>
        -min_routing_layer <min_layer>
        -max_routing_layer <max_layer>
        -pitches_in_tile <pitches>
        -layers_adjustments <list_of_layers_to_adjust>
        -regions_adjustments <list_of_regions_to_adjust>
        -nets_alphas_priorities <list_of_alphas_per_net>
        -verbose <verbose>
        -unidirectional_routing
        -clock_net_routing

Options description:

  • capacity_adjustment: Set global capacity adjustment (e.g.: -capacity_adjustment 0.3)
  • min_routing_layer: Set minimum routing layer (e.g.: -min_routing_layer 2)
  • max_routing_layer: Set maximum routing layer (e.g.: max_routing_layer 9)
  • pitches_in_tile: Set the number of pitches inside a GCell
  • layers_adjustments: Set capacity adjustment to specific layers (e.g.: -layers_adjustments {{ } …})
  • regions_adjustments: Set capacity adjustment to specific regions (e.g.: -regions_adjustments { } …})
  • nets_alphas_priorities: Set alphas for specific nets when using clock net routing (e.g.: -nets_alphas_priorities {{ } …})
  • verbose: Set verbose of report. 0 for less verbose, 1 for medium verbose, 2 for full verbose (e.g.: -verbose 1)
  • unidirectional_routing: Activate unidirectional routing (flag)
  • clock_net_routing: Activate clock net routing (flag)
  • NOTE 1: if you use the flag unidirectional_routing, the minimum routing layer will be assigned as “2” automatically
  • NOTE 2: the first routing layer of the design have index equal to 1
  • NOTE 3: if you use the flag clock_net_routing, only guides for clock nets will be generated
Detailed Routing

Run

detailed_route -param <param_file>

Options description:

  • param_file: This file contains the parameters used to control the detailed router)

Capabilities/Limitations

Global Considerations

  • OpenROAD v1.0 production will be focused on the tapeout mentioned in the above introduction. Features will be implemented in priority order based on our sponsor requirement to make the chosen design manufacturable. In Phase 2 of the IDEA program, the OpenROAD tool feature set will be rounded out and more of the project’s flow and tool research objectives will be addressed.
  • Each new design enablement (foundry process/PDK, library, IPs) will require setup via configuration files, one-time characterizations, etc. as documented with the tool. Examples include (i) the setup of PDN generation, (ii) the creation of “wrapped LEF abstracts” for cells and/or macros to comply with Generic Node Enablement (see Routing, below), and (iii) the creation of characterized lookup tables to guide CTS buffering.

Supported Platforms

  • OpenROAD v1.0 will build on “bare metal”, CentOS 7 with required packages installed as specified in the README.
  • MacOS will also be supported.
  • Users with access to Docker will also be able to build on any machine using the included Dockerfile.

Design Partitioning and Logic Synthesis

  • Logic Synthesis (Yosys) will accept only hierarchical RTL Verilog.
  • SystemVerilog to Verilog conversion must be performed by the user (e.g., using bsg sv2v or any tool of their choosing) before running Yosys.
  • Logic Synthesis is one of potentially multiple steps in OpenROAD that may require a single merged LEF as of the v1.0 release. A utility script to perform merging is here.
  • To support convergence in the downstream place-CTS-route steps, it is advisable to exclude cells that risk difficult pin access (e.g., sub-X1 sizes) and/or to invoke cell padding during placement. The cell exclusion would be akin to a “dont_use” list, which is not currently supported and must be manually implemented by editing the library files.

STA

  • Supports multi-corner analysis (e.g., setup and hold), but with limit of one mode.
  • SDC support up to latest public, open version (e.g., SDC 1.4).
  • No SI analysis: any coupling caps can be multiplied by a “Miller Coupling Factor” (MCF) and then treated as grounded.
  • No CCS/ECSM (current-source model) support.
  • No LVF support.
  • No PBA analysis option.
  • No instance IR drop (i.e., setting a rail voltage for given instance).
  • No reduction of non-tree wiring topologies. (Arnoldi reduction provided along with O’Brien-Savarino, 2-pole, Elmore reduction and delay calculation options.)

Floorplan

  • Macro placement is limited to 100 RAMs/macros per P&R block.
  • PDN configuration files must be provided by the user. These are documented in the “pdngen” tool repo, here.

Placement

  • A P&R block is limited to one logic power domain and one I/O power domain. Additional power domains must be handled manually (OpenROAD Tcl scripting).
  • Isolation cells, level converters and power management must be manually inserted into the layout by the user (e.g., as pre-placements).
  • No support of UPF/CPF formats for power intent.
  • Support of user guidance for logic clustering and placement will be limited to “fence” and “pre-placement” guidance, with the caveat that such guidance may degrade solution QOR in the OpenROAD flow.

Clock Tree Synthesis

  • Support only positive edge-triggered FFs
  • Hold buffering will be at post-CTS and not later in the flow

Routing

  • The TritonRoute router will not understand LEF57, LEF58 constructs in techlef: the workaround is OpenROAD Generic Node Enablement (see “OpenROAD Requirements for Generic Node Enablement, at this link).
  • Users should be advised that TritonRoute does not handle coloring explicitly; a color-correct-by-construction methodology (e.g., for Mx layers in 14/12nm) is achieved via Generic Node Enablement.
  • Antenna checking and fixing capability is committed for v1.0.

Layout Finishing and Final Verifications

  • Parasitic extraction (SPEF from layout) is unlikely to comprehend coupling.
  • There is no “signoff-quality electrical/performance analysis” counterpart to “PrimeTime-SI” (timing, signal integrity) or “Voltus”/“RedHawk” (power integrity).
  • A golden PV tool will be the evaluator for DRC.
  • Generation of merged GDS currently requires a Magic 8.2 tech file. Details are given here.
  • Export of merged GDS does not add text markings that may be expected by commercial physical verification tools.
  • For supported design tape-outs (particularly, at a commercial 14/12nm node, up through July 2020), physical verification (DRC/LVS) is expected to be performed by the design team using commercial tools. (Everything up to routed DEF and merged GDS will be produced by OpenROAD or other open-source tools.)

Developer Guide

Tool Philosophy

OpenROAD is a tool to build a chip from a synthesized netlist to a physical design for manufacturing.

The unifying principle behind the design of OpenROAD is for all of the tools to reside in one tool, with one process, and one database. All tools in the flow should use Tcl commands exclusively to control them instead of external “configuration files”. File based communication between tools and forking processes is strongly discouraged. This architecture streamlines the construction of a flexible tool flow and minimizes the overhead of invoking each tool in the flow.

Tool File Organization

Every tool follows the following file structure, grouping sources, tests and headers together.

src/CMakelists.txt - add_subdirectory's src/CMakelists.txt
src/tool/src/ - sources and private headers
src/tool/src/CMakelists.txt
src/tool/include/tool/ - exported headers
src/tool/test/
src/tool/test/regression

OpenROAD repository

CMakeLists.txt - top level cmake file
src/Main.cc
src/OpenROAD.cc - OpenROAD class functions
src/OpenROAD.i - top level swig, %includes tool swig files
src/OpenROAD.tcl - basic read/write lef/def/db commands
include/openroad/OpenRoad.hh - OpenROAD top level class, has instances of tools
include/openroad/Error.hh - Error reporting API

Some tools such as OpenDB are submodules, which are simply subdirectories in /src that are pointers to the git submodule. They are intentionally not segregated into a separate /module.

The use of submodules for new code integrated into OpenROAD is strongly discouraged. Submodules make changes to the underlying infrastructure (OpenDB, OpenSTA etc) difficult to propagate across the dependent submodule repositories. Submodules: just say no.

Where external/third party code that a tool depends on should be placed depends on the nature of the dependency.

  • Libraries - code packaged as a linkable library. Examples are tcl, boost, zlib, eigen, lemon, spdlog.

These should be installed in the build environment and linked by OpenRoad. Document these dependencies in the top level README.md file. The Dockerfile should be updated to illustrate where to find the library and how to install it. Adding libraries to the build enviroment requires coodination with the sys admins for the continuous integration hosts to make sure the environments include the dependency. Advanced notification should also be given to the development team so their private build environments can be updated.

Each tool cmake file builds a library that is linked by the OpenROAD application. The tools should not define a main() function. If the tool is tcl only and has no c++ code it does not need to have a cmake file. Tool cmake files should not include the following:

  • cmake_minimum_required
  • GCC_COVERAGE_COMPILE_FLAGS
  • GCC_COVERAGE_LINK_FLAGS
  • CMAKE_CXX_FLAGS
  • CMAKE_EXE_LINKER_FLAGS

None of the tools have commands to read or write LEF, DEF, Verilog or database files. These functions are all provided by the OpenROAD framework for consistency.

Tools should package all state in a single class. An instance of each tool class resides in the top level OpenROAD object. This allows multiple tools to exist at the same time. If any tool keeps state in global variables (even static) only one tool can exist at a time. Many of the tools being integrated were not built with this goal in mind and will only work on one design at a time. Eventually all of the tools should be upgraded to remove this deficiency as they are re-written to work in the OpenROAD framework.

Each tool should use a unique namespace for all of its code. The same namespace should be used for Tcl functions, including those defined by a swig interface file. Internal Tcl commands stay inside the namespace, and user visible Tcl commands should be defined in the global namespace. User commands should be simple Tcl commands such as ‘global_placement’ that do not create tool instances that must be based to the commands. Defining Tcl commands for a tool class is fine for internals, but not for user visible commands. Commands have an implicit argument of the current OpenROAD class object. Functions to get individual tools from the OpenROAD object can be defined.

Initialization (c++ tools only)

The OpenRoad class has pointers to each tools with functions to get each tool. Each tool has (at a minimum) a function to make an instance of the tool class, and an initialization function that is called after all of the tools have been made, and a funtion to delete the tool. This small header does not include the class definition for the tool so that the OpenRoad framework does not have to know anything about the tool internals or include a gigantic header file.

MakeTool.hh defines the following:

Tool *makeTool();
void initTool(OpenRoad *openroad);
void deleteTool(Tool *tool);

The OpenRoad::init() function calls all of the makeTool functions and then all of the initTool() functions. The init functions are called from the bottom of the tool dependences. Each init function grabs the state it needs out of the OpenRoad instance.

Commands

Tools should provide Tcl commands to control them. Tcl object based tool interfaces are not user friendly. Define Tcl procedures that take keyword arguments that reference the OpenRoad object to get tool state. OpenSTA has Tcl utilities to parse keyword arguements (sta::parse_keyword_args). See OpenSTA/tcl/*.tcl for examples. Use swig to define internal functions to C++ functionality.p

Tcl files can be included by encoding them in cmake into a string that is evaluated at run time (See Resizer::init()).

Errors

Tools should report errors to the user using the ord::error function defined in include/openroad/Error.hh. ord::error throws ord::Exception. The variables ord::exit_on_error and ord::file_continue_on_error control how the error is handled. If ord::exit_on_error is true OpenROAD reports the error and exits. If the error is encountered while reading a file with the source or read_sdc commands and ord::file_continue_on_error is false no other commands are read from the file. The default values of both variables is false.

Test

Each “tool” has a /test directory containing a script nameed “regression” to run “unit” tests. With no arguments it should run default unit tests.

No database files should be in tests. Read LEF/DEF/Verilog to make a database.

The regression script should not depend on the current working directory. It should be able to be run from any directory. Use filenames relative to the script name rather the the current working directory.

Regression scripts should print a consise summary of test failures. The regression script should return an exit code of zero if there are no errors and 1 if there are errors. The script should not print thousands of lines of internal tool info.

Regression scripts should pass the -no_init option to openroad so that a user’s init file is not sourced before the tests runs.

Regression scripts should add output files or directories to .gitignore so that running does note leave the source repository “dirty”.

The Nangate45 open source library data used by many tests is in test/Nangate45. Use the following command to add a link in the tool command

cd tool/test
ln -s ../../../test/Nangate45

After the link is installed, the test script can read the liberty file with the command shown below.

read_liberty Nangate45/Nangate45_typ.lib

Builds

Checking out the OpenROAD repo with –recursive installs all of the OpenRoad tools and their submodules.

git clone --recusive https://github.com/The-OpenROAD-Project/OpenROAD.git
cd OpenROAD
mkdir build
cd build
cmake ..
make

All tools build using cmake and must have a CMakeLists.txt file in their tool directory.

This builds the openroad executable in /build.

Note that removing submodules from a repo when moving it into OpenROAD is less than obvious. Here are the steps:

git submodule deinit <path_to_submodule>
git rm <path_to_submodule>
git commit-m "Removed submodule "
rm -rf .git/modules/<path_to_submodule>

Tools should compile with no compile warnings in gcc or clang with -Wall.

Tool Work Flow

To work on one of the tools inside OpenROAD when it is a submodule requires updating the OpenROAD repo to integrate your changes. Submodules point to a specific version (hash) of the submodule repo and do not automatically track changes to the submodule repo.

Work on OpenROAD should be done in the openroad branch. Stable commits on the openroad branch are periodically pushed to the master branch for public consumption.

To make changes to a submodule, first check out a branch of the submodule (git clone –recursive does not check out a branch, just a specific commit).

cd src/<tool>
git checkout <branch>

<branch> is the branch used for development of the tool when it is inside OpenROAD. The convention is for to be named ‘openroad’.

After making changes inside the tool source tree, stage and commit them to the tool repo and push them to the remote repo.

git add ...
git commit -m "massive improvement"
git push

If instead you have done development in a different branch or source tree, merge those changes into the branch used for OpenROAD.

Once the changes are in the OpenROAD submodule source tree it will show them as a diff in the hash for the directory.

cd openroad
git stage <tool_submodule_dir>
git commit -m "merge tool massive improvement"
git push

Example of Adding a Tool to OpenRoad

The patch file “add_tool.patch” illustrates how to add a tool to OpenRoad. Use

patch -p < doc/add_tool.patch`
cd src/tool/test
ln -s ../../../test/regression.tcl regression.tcl

to add the sample tool. This adds a directory OpenRoad/src/tool that illustrates a tool named “Tool” that uses the file structure described and defines a command to run the tool with keyword and flag arguments as illustrated below:

% toolize foo
Helping 23/6
Gotta pos_arg1 foo
Gotta param1 0.000000
Gotta flag1 false

% toolize -flag1 -key1 2.0 bar
Helping 23/6
Gotta pos_arg1 bar
Gotta param1 2.000000
Gotta flag1 true

% help toolize
toolize [-key1 key1] [-flag1] pos_arg1

Documentation

Tool commands should be documented in the top level OpenROAD README.md file. Detailed documentation should be the tool/README.md file.

Tool Flow

  • Verilog to DB (dbSTA)
  • Init Floorplan (OpenROAD)
  • I/O placement (ioPlacer)
  • PDN generation (pdngen)
  • Tapcell and Welltie insertion (tapcell)
  • I/O placement (ioPlacer)
  • Macro placement (TritonMacroPlace)
  • Global placement (RePlAce)
  • Gate Resizing and buffering (Resizer)
  • Detailed placement (OpenDP)
  • Clock Tree Synthesis (TritonCTS)
  • Repair Hold Violations (Resizer)
  • Global route (FastRoute)
  • Detailed route (TritonRoute)
  • Final timing/power report (OpenSTA)

Tool Checklist

Tools should make every attempt to minimize external dependencies. Linking libraries other than those currently in use complicates the builds and sacrifices the portability of OpenROAD. OpenROAD should be portable to many different compiler/operating system versions and dependencies make this vastly more complicated.

OpenROAD submodules reference tool openroad branch head. No git develop, openroad_app, or openroad_build branches.

Submodules used by more than one tool belong in /src, not duplicated in each tool repo.

CMakeLists.txt does not use add_compile_options include_directories link_directories link_libraries Use target_ versions instead. See https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1

CMakeLists.txt does not use glob. Use explicit lists of source files and headers instead.

CMakeLists.txt does not define CFLAGS CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE Let the top level and defaults control these.

No main.cpp or main procedure.

No compiler warnings for gcc or clang with optimization enabled.

Does not call flute::readLUT (called once by OpenRoad).

Tcl command(s) documented in top level README.md in flow order.

Command line tool documentation in tool README.

Conforms to Tcl command naming standards (no camel case).

Does not read configuration files. Use command arguments or support commands.

.clang-format at tool root directory to aid foreign programmers.

No jenkins/, Jenkinsfile, Dockerfile in tool directory.

regression script named “test/regression” with no arguments that runs tests. Not tests/regression-tcl.sh, not test/run_tests.py etc.

regression script should run independent of current directory. For example, ../test/regression should work.

regression should only print test results or summary, not belch 1000s of lines of output.

Test scripts use OpenROAD tcl commands (not itcl, not internal accessors).

regression script should only write files in a directory that is in the tool’s .gitignore so the hierarchy does not have modified files in it as a result or running the regressions.

Regressions report no memory errors with valgrind (stretch goal).

Regressions report no memory leaks with valgrind (difficult).

James Cherry, Dec 2019

Coding Practices

Note

This is a compilation of many idioms in openroad code that I consider undesirable. Obviously other programmers have different opinions or they would not be so pervasive. James Cherry 04/2020

C++

Practice #1

Don’t comment out code. Remove it. git provides a complete history of the code if you want to look backwards. Huge chunks of commented out code that are stunningly common in student code makes it nearly impossible to read.

FlexTa.cpp has 220 lines of code and 600 lines of commented out code.

Practice #2

Don’t use prefixes on function names or variables. That’s what namespaces are for.

namespace fr {
      class frConstraint
      class frLef58CutClassConstraint
      class frShortConstraint
      class frNonSufficientMetalConstraint
      class frOffGridConstraint
      class frMinEnclosedAreaConstraint
      class frMinStepConstraint
      class frMinimumcutConstraint
      class frAreaConstraint
      class frMinWidthConstraint
      class frLef58SpacingEndOfLineWithinEndToEndConstraint
      class frLef58SpacingEndOfLineWithinParallelEdgeConstraint
      class frLef58SpacingEndOfLineWithinMaxMinLengthConstraint
      class frLef58SpacingEndOfLineWithinConstraint
      class frLef58SpacingEndOfLineConstraint
}
Practice #3

Namespaces should be all lower case and short. This is an example of a poor choice: namespace TritonCTS

Practice #4

Don’t use extern on function definitions. It is pointless in a world with prototypes.

namespace fr {
      extern frCoord getGCELLGRIDX();
      extern frCoord
      getGCELLGRIDY();
      extern frCoord getGCELLOFFSETX();
      extern frCoord
      getGCELLOFFSETY();
}
Practice #5

Don’t use prefixes on file names. That’s what directories are for.

frDRC.h frDRC_init.cpp frDRC_main.cpp frDRC_setup.cpp frDRC_util.cpp
Practice #6

Don’t name variables theThingy, curThingy or myThingy. It is just distracting extraneous verbage. Just use thingy.

float currXSize;
float currYSize;
float currArea;
float currWS;
float currWL;
float currWLnoWts;
Practice #7

Do not use global varaibles. All state should be inside of classes. Global variables make multi-threading next to impossible and preclude having multiple copies of a tool running in the same process. The only global variable in OpenRoad should be the singleton that tcl commands reference.

extern std::string DEF_FILE;
extern std::string GUIDE_FILE;
extern std::string OUTGUIDE_FILE;
extern std::string LEF_FILE;
extern std::string OUTTA_FILE;
extern std::string OUT_FILE;
extern std::string DBPROCESSNODE;
extern std::string OUT_MAZE_FILE;
extern std::string DRC_RPT_FILE;
extern int MAX_THREADS ;
extern int VERBOSE ;
extern int BOTTOM_ROUTING_LAYER;
extern bool ALLOW_PIN_AS_FEEDTHROUGH;
extern bool USENONPREFTRACKS;
extern bool USEMINSPACING_OBS;
extern bool RESERVE_VIA_ACCESS;
extern bool ENABLE_BOUNDARY_MAR_FIX;
Practice #8

Do not use strings (names) to refer to database or sta objects except in user interface code. DEF, SDC, and verilog all use different names for netlist instances and nets so the names will not always match.

Practice #9

Do not use continue. Wrap the body in an if instead.

// instead of
for(dbInst* inst : block->getInsts() ) {
      // Skip for standard cells
      if( (int)inst->getBBox()->getDY() <= cellHeight) { continue; }
      // code
}
// use
for(dbInst* inst : block->getInsts() ){
      // Skip for standard cells
      if( (int)inst->getBBox()->getDY() > cellHeight) {
            // code
      }
}
Practice #10

Don’t put magic numbers in the code. Use a variable with a name that captures the intent. Document the units if they exist.

examples of unnamed magic numbers:

Practice #11

Don’t copy code fragments. Write functions.

// 10x
int x_pos = (int)floor(theCell->x_coord / wsite + 0.5);
// 15x
int y_pos = (int)floor(y_coord / rowHeight + 0.5);

// This
nets[newnetID]->netIDorg = netID;
nets[newnetID]->numPins = numPins;
nets[newnetID]->deg = pinInd;
nets[newnetID]->pinX = (short *)malloc(pinInd* sizeof(short));
nets[newnetID]->pinY = (short *)malloc(pinInd* sizeof(short));
nets[newnetID]->pinL = (short *)malloc(pinInd* sizeof(short));
nets[newnetID]->alpha = alpha;

// Should factor out the array lookup.
Net *net = nets[newnetID];
net->netIDorg = netID;
net->numPins = numPins;
net->deg = pinInd;
net->pinX = (short*)malloc(pinInd* sizeof(short));
net->pinY = (short *)malloc(pinInd* sizeof(short));
net->pinL = (short *)malloc(pinInd* sizeof(short));
net->alpha = alpha;

// Same here:
if(grid[j][k].group != UINT_MAX) {
  if(grid[j][k].isValid == true) {
    if(groups[grid[j][k].group].name == theGroup->name)
      area += wsite * rowHeight;
  }
}
Practice #12

Don’t use logical operators to test for null pointers.

if (!net) {
      // code
}

// should be
if (net != nullptr) {
      // code
}
Practice #13

Don’t use malloc. Use new. We are writting C++, not C.

Practice #14

Don’t use C style arrays. There is no bounds checks for them so they invite subtle memory errors to unwitting programmers that fail to use valgrind. Use std::vector or std::array.

Practice #15

Break long functions into smaller ones, preferably that fit on one screen.

  • 162 lines void DBWrapper::initNetlist()
  • 246 lines static vector<pair<Partition, Partition>> GetPart()
  • 263 lines void MacroCircuit::FillVertexEdge()
Practice #16

Don’t reinvent functions like round, floor, abs, min, max. Use the std versions.

int size_x = (int)floor(theCell->width / wsite + 0.5);
Practice #17

Don’t use C stdlib.h abs, fabs or fabsf. They fail miserably if the wrong arg type is passed to them. Use std::abs.

Practice #18

Fold code common to multiple loops into the same loop. Each of these functions loops over every instance like this:

legal &= row_check(log);
legal &= site_check(log);
for(int i = 0; i < cells.size(); i++) {
      cell* theCell = &cells[i];
      legal &= power_line_check(log);
      legal &= edge_check(log);
      legal &= placed_check(log);
      legal &= overlap_check(log);
}
// with this loop
for(int i = 0; i < cells.size(); i++) {
      cell* theCell = &cells[i];
}

Instead make one pass over the instances doing each check.

Practice #19

Don’t use == true, or == false. Boolean expressions have a value of true or false already.

if(found.first == true) {
      // code
}
// is simply
if(found.first) {
      // code
}
// and
if(found.first == false) {
      // code
}
// is simply
if(!found.first) {
      // code
}
Practice #20

Don’t nest if statements. Use && on the clauses instead.

if(grid[j][k].group != UINT_MAX)
  if(grid[j][k].isValid == true)
    if(groups[grid[j][k].group].name == theGroup->name)

is simply

if(grid[j][k].group != UINT_MAX
   && grid[j][k].isValid
   && groups[grid[j][k].group].name == theGroup->name)
Practice #21

Don’t call return at the end of a function that does not return a value.

Practice #22

Don’t use <>’s to include anything but system headers. Your project’s headers should NEVER be in <>’s. - https://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html - https://stackoverflow.com/questions/21593/what-is-the-difference-between-include-filename-and-include-filename

These are all wrong: .. code-block:: cpp

#include <opendb/db.h> #include <ABKCommon/uofm_alloc.h> #include <OpenSTA/liberty/Liberty.hh> #include <opendb/db.h> #include <opendb/dbTypes.h> #include <opendb/defin.h> #include <opendb/defout.h> #include <opendb/lefin.h>
Practice #23

Don’t make “include the kitchen sink” headers and include them in every source file. This is convenient (lazy) but slows the builds down for everyone. Make each source file include just the headers it actually needs.

// Types.hpp
#include <OpenSTA/liberty/Liberty.hh>
#include <opendb/db.h>
#include <opendb/dbTypes.h>
// It should be obvious that every source file is not reading def.
#include <opendb/defin.h>
// or writing it.
#include <opendb/defout.h>
#include <opendb/lefin.h>
#include "db_sta/dbNetwork.hh"
#include "db_sta/dbSta.hh"

Note this example also incorrectly uses <>’s around openroad headers.

Header files should only include files to support the header. Include files necessary for code in the code file, not the header.

In the example below NONE of the system files listed are necessary for the header file.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <limits.h>

unsigned num_nets = 1000;
unsigned num_terminals = 64;
unsigned verbose = 0;
float alpha1 = 1;
float alpha2 = 0.45;
float alpha3 = 0;
float alpha4 = 0;
float margin = 1.1;
unsigned seed = 0;
unsigned root_idx = 0;
unsigned dist = 2;
float beta = 1.4;
bool runOneNet = false;
unsigned net_num = 0;
Practice #24

Use class declarations if you are only refering to object by pointer instead of including their complete class definition. This can vastly reduce the code the compiler has to process.

class Network;
// instead of
#include "Network.hh"
Practice #25

Use pragma once instead of #define to protect headers from being read more than once. The #define symbol has to be unique, which is difficult to guarantee.

// Instead of:
#ifndef __MACRO_PLACER_HASH_UTIL__
      #define __MACRO_PLACER_HASH_UTIL__
#endif
// use
#pragma once
Practice #26

Don’t put “using namespace” inside a function. It makes no sense what so ever but I have seen some very confused programmers do this far too many times.

Practice #27

Don’t nest namespaces. We don’t have enough code to justify that complication.

Practice #28

Don’t use using namespace It is just asking for conflicts and doesn’t explicity declare what in the namespace is being used. Use using namespace::symbol; instead. And especially NEVER EVER EVER using namespace std. It is HUGE.

The following is especially confused because it is trying to “use” the symbols in code that is already in the MacroPlace namespace.

using namespace MacroPlace;

namespace MacroPlace { }
Practice #29

Use nullptr instead of NULL. This is the C++ approved version of the ancient C #define.

Practice #30

Use range iteration. C++ iterators are ugly and verbose.

// Instead of
odb::dbSet::iterator nIter;
for (nIter = nets.begin(); nIter != nets.end(); ++nIter) {
      odb::dbNet* currNet = *nIter;
      // code
}
// use
for (odb::dbNet* currNet : nets) {
      // code
}
Practice #34

Don’t use end of line comments unless they are very short. Don’t assume that the person reading your code has a 60” monitor.

for (int x = firstTile._x; x <= lastTile._x; x++) { // Setting capacities of edges completely inside the adjust region according the percentage of reduction
      // code
}
Practice #35

Don’t std::pow for powers of 2 or for decimal constants.

// This
double newCapPerSqr = (_options->getCapPerSqr() * std::pow(10.0, -12));
// Should be
double newCapPerSqr = _options->getCapPerSqr() * 1E-12;

// This
unsigned numberOfTopologies = std::pow(2, numberOfNodes);
// Should be
unsigned numberOfTopologies = 1 << numberOfNodes;

Git

Practice #31

Don’t put /’s in .gitignore directory names. test/

Practice #32

Don’t put file names in .gitignore ignored directories. test/results test/results/diffs

Practice #33

Don’t list compile artifacts in .gitignore They all end up in the build directory so each file type does not have to appear in .gitignore.

All of the following is nonsense that has propagated faster than covid in student code:

Compiled Object files

*.slo *.lo *.o *.obj

Precompiled Headers

*.gch *.pch

Compiled Dynamic libraries

*.so *.dylib *.dll

Fortran module files

*.mod *.smod

Compiled Static libraries

*.lai *.la *.a *.lib

CMAKE

Practice #35

Don’t change compile flags in cmake files. These are set at the top level and should not be overriden.

set(CMAKE_CXX_FLAGS "-O3")
set(CMAKE_CXX_FLAGS_DEBUG "-g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
Practice #36

Don’t put /’s in cmake directory names. Cmake knows they are directories.

target_include_directories( ABKCommon PUBLIC ${ABKCOMMON_HOME} src/ )
Practice #37

Don’t use glob. Explictly list the files in a group.

# Instead of
file(GLOB_RECURSE SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# should be
list(REMOVE_ITEM SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/Main.cpp)
list(REMOVE_ITEM SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/Parameters.h)
list(REMOVE_ITEM SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/Parameters.cpp)

Database Math 101

DEF defines the units it uses with the units command.

UNITS DISTANCE MICRONS 1000 ;

Typically the units are 1000 or 2000 database units (DBU) per micron. DBUs are integers, so the distance resolution is typically 1/1000u or 1nm.

OpenDB uses an int to represent a DBU, which on most hardware is 4 bytes. This means a database coordinate can be +/-2147483647, which is about 2 billion units, corresponding to 2 million microns or 2 meters.

Since chip coordinates cannot be negative, it would make sense to use an unsigned int to represent a distance. This conveys the fact that it can never be negative and doubles the maximum possible distance that can be represented. The problem is doing subtraction with unsigned numbers is dangerous because the differences can be negative. An unsigned negative number looks like a very very big number. So this is a very bad idea and leads to bugs.

Note that calculating an area with int values is problematic. An int * int does not fit in an int. My suggestion is to use int64_t in this situation. Although long “works”, it’s size is implementation dependent.

Unfortunately I have seen multiple instances of programs using a double for distance calculations. A double is 8 bytes, with 52 bits used for the mantissa. So the largest possible integer value that can be represented without loss is 5e+15, 12 bits less than using a int64_t. Doing an area calculation on a large chip that is more than sqrt(5e+15) = 7e+7 DBU will overflow the mantissa and truncate the result.

Not only is a double less capable than an int64_t, using it the tells any reader of the code that the value can be real number, such as 104.23. So it is extremely misleading.

Circling back to LEF, we see that unlike DEF the distances are real numbers like 1.3 even though LEF also has a distance unit statement. I suspect this is a historical artifact of a mistake made in the early definition of the LEF file format. The reason it is a mistake is because decimal fractions cannot be represented exactly in binary floating point. For example, 1.1 = 1.00011001100110011…, a continued fracion.

OpenDB uses int to represent LEF distances, just like DEF. This solves the problem by multiplying distances by a decimal constant (distance units) to convert the distance to an integer. In the future I would like to see OpenDB use a dbu typedef instead of int everywhere.

Unfortunately, I see RePlAce, OpenDP, TritonMacroPlace and OpenNPDN all using double or float to represent distances and converting back and forth between DBUs and microns everywhere. This means they also need to round or floor the results of every calculation because the floating point representation of the LEF distances is a fraction that cannot be exactly represented in binary. Even worse is the practice of reinventing round in the following idiom.

(int) x_coord + 0.5

Even worse than using a double is using float because the mantissa is only 23 bits, so the maximum exactly representable integer is 8e+6. This makes it even less capable than an int.

When a value has to be snapped to a grid such as the pitch of a layer the calculation can be done with a simple divide using ints, which floors the result. For example, to snap a coordinate to the pitch of a layer the following can be used.

int x, y;
inst->getOrigin(x, y);
int pitch = layer->getPitch();
int x_snap = (x / pitch) * pitch;

The use of rounding in existing code that uses floating point representations is to compensate for the inability to represent floating point fractions exactly. Results like 5.99999999992 need to be “fixed”. This problem does not exist if fixed point arithmetic is used.

The only place that the database distance units should appear in any program should be in the user interface, because humans like microns more than DBUs. Internally code should use int for all database units and int64_t for all area calculations.

James Cherry, 2019

Getting Involved

Thank you for taking the time to read this document and to contribute, the OpenROAD project will not reach all of its objectives without help!

Possible ways to contribute

  • Open Source PDK information
  • Open Source Designs
  • Useful scripts
  • Tool improvements
  • New tools
  • Improving documentation including this document
  • Star our project and repos so we can see the number of people interested

Licensing Contributions

As much as possible, all contributions should be licensed using the BSD3 license. You can propose another license if you must but contributions made with BSD3 fit in the spirit of OpenROAD’s permissively open source philosophy. We do have exceptions in the project but over time we hope that all contributions will be BSD3, or some other permissive license.

Contributing Open Source PDK information and Designs

If you have new design or PDK information to contribute, please add this to the repo OpenROAD-flow-scripts. In the flow directory you will see a directory for designs with Makefiles to run them, and one for PDK platforms used by the designs. If you add a new PDK platform be sure to add at least one design that uses it.

Contributing Scripts and Code

We follow the Google C++ style guide. If you find code that is not following this guide, within each file that you edit, follow the style in that file. Please pay careful attention to the Tool Checklist for all code. If you want to add or improve functionality in OpenROAD please start with the top level app repo. You can see in the src directory that submodules exist pointing to tested versions of the other relevant repos in the project. Please look at the tool workflow in the developer guide document to work with the app and its submodule repos in an efficient way.

Please pay attention to the test directory and be sure to add tests for any code changes that you make with open sourceable PDK and design information. We provide the nandgate45 PDK in the OpenROAD-flow-scripts repo to help with this. Pull requests with code changes are unlikely to be accepted without accompanying test cases. There are many examples tests. Each repo has a test directory as well with tests you should run and add to if you modify something in one of the submodules.

For changes that claim to improve QoR or PPA, please run many tests and ensure that the improvement is not design specific. There are designs in the OpenROAD-flow-scripts repo which can be used unless the improvement is technology specific.

Do not add runtime or build dependencies without serious thought. For a project like OpenROAD with many application sub components, the software architecture can quickly get out of control. Changes with lots of new dependencies which are not necessary are less likely to be integrated.

If you want to add TCL code to define a new tool command look at pdngen as an example of how to do so. Take a look at the cmake file which automatically sources the tcl code and the tcl code itself.

Questions

You can file git issues to ask questions, file issues or you can contact us via email openroad at eng.ucsd.edu

Using the logging infrastructure

In order to ensure consistent messaging from the openroad application we have adopted spdlog as our logging infrastructure. We have a thin wrapper on top for extensibility. Whenever a message needs to be issued you will use one of the logging functions in the ‘ord’ namespace.

All output from OpenROAD tools should be directed through the logging API so that redirection, file logging and execution control flow is handled consistently.

The logging infrastructure also supports generating a JSON file containing design metrics (e.g. area, slack). This output is directed to a user specified file. The openroad application take a “-metrics ” command line argument to specify the file.

Message Types

Report

Reports are tool output in the form of a report to the user. Examples are timing paths, or power analysis results. Tool reports that use ‘printf’ or c++ streams should use the report message API instead.

Debug

Debug messages are only of use to tool developers and not to end users. These messages are not shown unless explicitly enabled.

Information

Information messages may be used for reporting metrics, quality of results, or program status to the user. Any messages which indicate runtime problems, such as potential faulty input or other internal program issues, should be issued at a higher status level.

Example messages for this status level:

Number of input ports: 47
Running optimization iteration 2
Current cell site utilization: 57.1567%
Warning

Warnings should be used for indicating atypical runtime conditions that may affect quality, but not correctness of the output. Any conditions that affect correctness should be issued at a higher status level.

Example warning messages:

Core area utilization is greater than 90%. The generated cell placement may not be routable.
14 outputs are not constrained for max capacitance.
Pin ‘A[0]’ on instance ‘mem01’ does not contain antenna information and will not be checked for antenna violations.
Error

Error messages should be used for indicating correctness problems. Problems with command arguments are a good example of errors. Errors exit the current command by throw an error that can be caught in a Tcl command script. Errors that occur while reading a command file stop executing the script commands.

Example error messages:

Invalid selection: net ‘test0’ does not exist in the design.
Cell placement cannot be run before floorplanning.
Argument ‘max_routing_layer’ expects an integer value from 1 to 10.
Critical

Critical messages should be used for indicating correctness problems that the program is not able to work around or ignore, and require immediate exiting of the program (abort).

Example critical messages:

Database ‘chip’ has been corrupted and is not recoverable.
Unable to allocate heap memory for array ‘vertexIndices’. The required memory size may exceed host machine limits.
Assertion failed: ‘nodeVisited == false’ on line 122 of example.cpp. Please file a Github issue and attach a testcase.

Coding

Each status message requires: * The three letter tool ID * The message ID * The message string * Optionally, additional arguments to fill in placeholders in the message string

Reporting is simply printing and does not require a tool or message ID. The tool ID comes from a fixed enumeration of all the tools in the system. This enumeration is in Logger.h. New abbreviations should be added after discussion with the system architects. The abbreviation matches the c++ namespace for the tool.

Message IDs are integers. They are expected to be unique for each tool. This has the benefit that a message can be mapped to the source code unambiguously even if the text is not unique. Maintaining this invariant is the tool owner’s responsibility. To ensure that the IDs are unique each tool should maintain a file named ‘messages.txt’ in the top level tool directory listing the message IDs along with the format string. When code that uses a message ID is removed the ID should be retired by removing it from ‘messages.txt’. See the tuility etc/find_messages.py to scan a tool directory and write a messages.txt file.

Spdlog comes with the fmt library which supports message formatting in a python / c++20 like style.

The message string should not include the tool ID or message ID which will automatically be prepended. A trailing new line will automatically be added so messages should not end with one. Messages should be written as complete sentences and end in a period. Multi-line messages may contain embedded new lines.

Some examples:

logger->report(“Path startpoint: {}”, startpoint);
logger->error(ODB, 25, “Unable to open LEF file {}.”, file_name);
logger->info(DRT, 42, “Routed {} nets in {:3.2f}s.”, net_count, elapsed_time);

Tcl functions for reporting messages are defined in the OpenRoad swig file OpenRoad.i. The message is simply a Tcl string (no c++20 formatting). The logger for Tcl functions The above examples in Tcl are shown below.

utl::report “Path startpoint: $startpoint”
utl::error ODB 25 “Unable to open LEF file $file_name.”
utl::info DRT 42 “Routed $net_count nets in [format %3.2f $elapsed_time].”

utl::report should be used instead of ‘puts’ so that all output is logged.

Calls to the Tcl functions utl::warn and utl::error with a single message argument report with tool ID “UKN” and message ID 0000.

Tools #include utl/Logger.h that defines the logger API. The Logger instance is owned by the OpenRoad instance. Each tool should retrieve the logger instance in the tool init function called after the tool make function by the OpenRoad application.

Every tool swig file must include src/Exception.i so that errors thrown by utl::error are caught at the Tcl command level. Use the following swig command before %inline.

%include "../../Exception.i"

The logger functions are shown below.

Logger::report(const std::string& message,
               const Args&... args)
Logger::info(ToolId tool,
             int id,
             const std::string& message,
             const Args&... args)
Logger::warn(ToolId tool,
             int id,
             const std::string& message,
             const Args&... args)
Logger::error(ToolId tool,
              int id,
              const std::string& message,
              const Args&... args)
Logger::critical(ToolId tool,
                 int id,
                 const std::string& message,
                 const Args&... args)

The corresponding Tcl functions are shown below.

utl::report message
utl::info tool id message
utl::warn tool id message
utl::error tool id message
utl::critical tool id message

Although there is a utl::critical function, it is really difficult to imagine any circumstances that would justify aborting execution of the application in a tcl function.

Debug Messages

The debug message have a different programming model. As they are most often not issued the concern is to avoid slowing down normal execution. For this reason such messages are issued by using the debugPrint macro. This macro will avoid evaluating its arguments if they are not going to be printed. The API is:

debugPrint(logger, tool, group, level, message, ...)

The debug() method of the Logger class should not be called directly. No message id is used as these messages are not intended for end users. The level is printed as the message id in the output.

The argument types are as for the info/warn/error/ciritical messages. The one additional argument is group which is a const char*. Its purposes is to allow the enabling of subsets of messages within one tool.

Debug messages are enabled with the tcl command: set_debug_level <tool> <group> <level>

Metrics

The metrics logging uses a more restricted API since JSON only supports specific types. There are a set of overloaded methods of the form:

metric(ToolId tool,
       const std::string_view metric,
       <type> value)

where <type> can be int, double, string, or bool. This will result in the generated JSON:

"<tool>-<metric>" : value

String values will be enclosed in double-quotes automatically.

Converting to Logger

The error functions in include/openroad/Error.hh should no longer be included or used. Use the corresponding logger functions.

All uses of the tcl functions ord::error and ord::warn should be updated call the utl::error/warn with a Tool ID and message ID. For compatibility these are defaulted to ‘UKN’ and ‘0000’ until they are updated.

There is no reason to puts (ie, print) errors in regression tests that are caught. The logger prints the error now.

Init floorplan, openroad/src, init floorplan, dbSta, resizer, and opendp have been updated to use the Logger if you need examples of how to initialize and use it.

Regression tests should not have any UKN-0000 messages in their ok files. A simple grep should indicate that you still have pending calls to pre-logger error/warn functions. `

The cmake file for the tool must also be updated to include spdlog in the link libraries so it can find the header files if they are not in the normal system directories. dfm is an example of this problem; it has an ancient version of spdlog in ‘/usr/include/spdlog’. Use module to install spdlog 1.8.1 on dfm and check your build there.

target_link_libraries(<library_target>
  PUBLIC
    utl
)
Tool message/namespace
antenna_checker ant
dbSta sta
FastRoute grt
finale fin
flute3 stt
gui gui
ICeWall pad
init_fp ifp
ioPlacer ppl
OpenDB odb
opendp dpl
OpenRCX rcx
OpenROAD ord
OpenSTA sta
PartMgr par
pdngen pdn
PDNSim psm
replace gpl
resizer rsz
tapcell tap
TritonCTS cts
TritonMacroPlace mpl
TritonRoute drt
utility utl

FAQs

Where can I download OpenROAD tools?

Currently, we don’t provide pre-built binaries for the tools. You need to build the tools yourself on a supported platform. Please, refer to the Getting Started section to build the tools.

How can I contribute?

Thank you for your willingness to contribute. Please, see the Getting Involved guide.

Table Of Contents

Contents: