Skip to content

About

A High-performance Timing Analysis Tool for VLSI Systems

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Repository files navigation

OpenTimer

Build Status Standard Download Version AskMe Wiki License: MIT

A High-Performance Timing Analysis Tool for VLSI Systems

Why OpenTimer?

OpenTimer is a new static timing analysis (STA) tool to help IC designers quickly verify the circuit timing. It is developed completely from the ground up using C++17 to efficiently support parallel and incremental timing. Key features are:

  • Industry standard format (.lib, .v, .spef, .sdc) support
  • Graph- and path-based timing analysis
  • Parallel incremental timing for fast timing closure
  • Award-winning tools and golden timers in CAD Contests

Get Started with OpenTimer

The easiest way to start using OpenTimer is to use OpenTimer shell. OpenTimer shell is a powerful tool for interactive analysis and the simplest way to learn core functionalities. Compile OpenTimer and launch the shell program ot-shell under the bin directory.

~$ ./bin/ot-shell
  ____              _______              
 / __ \___  ___ ___/_  __(_)_ _  ___ ____
/ /_/ / _ \/ -_) _ \/ / / /  ' \/ -_) __/
\____/ .__/\__/_//_/_/ /_/_/_/_/\__/_/       v2
    /_/                                     
MIT License: type "license" to see more details.
For help, type "help".
For bug reports, issues, and manual, please see:
<https://github.com/OpenTimer/OpenTimer>.
ot> 

We have provided a simple design which consists of five gates (one NAND, one NOR, two INV, one FF) and one clock. Move to the folder and read this simple design.

ot> cd example/simple
ot> read_celllib osu018_stdcells.lib
ot> read_verilog simple.v   
ot> read_sdc simple.sdc

Report the timing to show the most critical path.

ot> report_timing      # report the most critical path
Startpoint    : inp1
Endpoint      : f1:D
Analysis type : min
------------------------------------------------------
       Type       Delay        Time   Dir  Description
------------------------------------------------------
       port       0.000       0.000  fall  inp1
        pin       0.000       0.000  fall  u1:A (NAND2X1)
        pin       2.786       2.786  rise  u1:Y (NAND2X1)
        pin       0.000       2.786  rise  u4:A (NOR2X1)
        pin       0.181       2.967  fall  u4:Y (NOR2X1)
        pin       0.000       2.967  fall  f1:D (DFFNEGX1)
    arrival                   2.967        data arrival time

related pin      25.000      25.000  fall  f1:CLK (DFFNEGX1)
 constraint       1.518      26.518        library hold_falling
   required                  26.518        data required time
------------------------------------------------------
      slack                 -23.551        VIOLATED

The critical path originates from the primary input inp1 and feed into the data pin f1:D of the flip-flop DFFNEGX1.

Compile OpenTimer

System Requirements

OpenTimer is very self-contained and has very few dependencies. This fork requires GCC or Clang with a C++20 standard library, including atomic wait/notify support used by the bundled Taskflow.

The CMake build and Tcl-based SDC reader also require a Tcl shell interpreter:

  • tclsh (most Unix/Linux/OSX distributions already include tclsh)

OpenTimer has been tested to run well on Linux distributions and MAC OSX.

Build through CMake

We use CMake to manage the source and tests. We recommend using out-of-source build.

~$ git clone https://github.com/OpenTimer/OpenTimer.git
~$ cd OpenTimer
~$ mkdir build
~$ cd build
~$ cmake ../ -DCMAKE_CXX_STANDARD=20
~$ make 

After successful build, you can find binaries and libraries in the folders bin and lib, respectively.

Build and test the library through Bazel

The fork also supports standalone Bazel builds on macOS and Linux, using a C++20 compiler (required by the bundled Taskflow). Bazelisk selects the version in .bazelversion; dependencies are pinned in MODULE.bazel.

bazel build -c opt //:opentimer //:taskflow
bazel test -c opt //...
bazel test -c dbg //...
# Run only the Liberty parsing and timing regressions:
bazel test -c opt //unittest:celllib_smoke

The small suite covers tokenization, path heaps, Liberty buses and lookup tables, mixed library units and slew thresholds, input drivers, ideal clocks, and using OpenTimer alongside the bundled Taskflow executor. Each local Bazel test has a 30-second timeout. The old unittest/unit.cpp contains only disabled tests and is not registered. CMake remains the build for the shell, examples, and TAU benchmark suite.

Bazel uses packages/ot/config.hpp, independently of CMake's generated ot/config.hpp. It does not require running CMake first. On macOS the local configuration selects Apple Command Line Tools.

LiveHD can continue using local_repository(name = "opentimer", path = "../OpenTimer") with the same public labels: @opentimer//:opentimer, @opentimer//:taskflow, and @opentimer//:srcs. Consumers select C++20 or newer in their own configuration; dependency .bazelrc files are not inherited. Code linked with OpenTimer must use @opentimer//:taskflow for <taskflow/taskflow.hpp> to avoid mixing incompatible Taskflow implementations in one binary. The OpenTimer-only regressions from LiveHD are also available here as @opentimer//unittest:celllib_smoke (requiring the consumer's @googletest).

Run Tests

OpenTimer uses Doctest for unit tests and TAU15 benchmarks for integration/regression tests. These benchmarks are generated by an industry standard timer and are being used by many EDA researchers.

~$ make test

Design Philosophy

OpenTimer has a unique software architecture to efficiently enable parallel incremental timing. We draw two levers on performance and useability by grouping each timing operation to one of the three categories, builder, action, and accessor.

Type Description Example Time Complexity
Builder create lazy tasks to build an analysis framework read_celllib, insert_gate, set_slew O(1)
Action carry out builder operations to update the timing update_timing, report_timing, report_slack Algorithm-dependent
Accessors inspect the timer without changing any internal data structures dump_timer, dump_slack, dump_net_load Operation-dependent

Builder: OpenTimer Lineage

OpenTimer maintains a lineage graph of builder operations to create a task execution plan (TEP). A TEP starts with no dependency and keeps adding tasks to the lineage graph every time you call a builder operation. It records what transformations need to be executed when an action has been called.

The above figure shows an example lineage graph of a sequence of builder operations. The cyan path is the main lineage line with additional tasks attached to enable parallel execution. OpenTimer use Taskflow to create dependency graphs.

Action: Update Timing

A TEP is materialized and executed when the timer is requested to perform an action operation. Each action operation triggers timing update from the earliest task to the one that produces the result of the action call. Internally, OpenTimer creates task dependency graph to update timing in parallel, including forward (slew, arrival time) and backward (required arrival time) propagations.

The figure above shows the dependency graph (forward in white, backward in cyan) to update the timing of the simple design. When an action call finishes, it cleans out the lineage graph with all timing up-to-date.

Accessor: Inspect OpenTimer

The accessor operations let you inspect the timer status and dump timing information. All accessor operations are declared as const methods in the timer class. Calling them promises not to alter any internal members. For example, you can dump the timing graph into a DOT format and use tools like GraphViz for visualization.

ot> dump_graph  
digraph TimingGraph {
  "inp1";
  ...  # skip for short
  "f1:CLK" -> "f1:Q";
  "u1:B" -> "u1:Y";
  "u1:A" -> "u1:Y";
}

OpenTimer Shell

OpenTimer shell is a powerful command line tool to perform interactive analysis. It is also the easiest way to get your first timing report off the ground. The program ot-shell can be found in the folder bin/ after you Compile OpenTimer.

Commands

The table below shows a list of commonly used commands.

Command type Arguments Description Example
read_celllib builder [-min | -max] file read the cell library for early and late splits read_celllib mylib.lib
read_verilog builder file read the verilog netlist read_verilog mynetlist.v
read_spef builder file read parasitics in SPEF read_spef myparasitics.spef
read_sdc builder file read a Synopsys Design Constraint file read_sdc myrule.sdc
update_timing action none update the timing update_timing
report_timing action [-num_paths k] report the critical paths report_timing -num_paths 10
report_tns action none report the total negative slack report_tns
report_wns action none report the worst negative slack report_wns
dump_graph accessor [-o file] dump the timing graph to a DOT format dump_graph
dump_timer accessor [-o file] dump the design statistics dump_timer

To see the full command list, visit OpenTimer Wiki.

Integrate OpenTimer to your Project

There are a number of ways to develop your project on top of OpenTimer.

Option 1: Add OpenTimer Subproject

The easiest way to build an OpenTimer application is to include it as a subproject using CMake add_subdirectory. Copy OpenTimer to your project root directory and configure your CMakeLists as follows:

cmake_minimum_required (VERSION 3.9)                  # CMake minimum version
project(app)                                          # your OpenTimer application
add_subdirectory(OpenTimer)                           # add OpenTimer project
include_directories(${PROJECT_SOURCE_DIR}/OpenTimer)  # add OpenTimer include

find_package(Threads REQUIRED)                        # thread library (pthread)

add_executable(app app.cpp)                           # link to your app.cpp
target_link_libraries(app OpenTimer Threads::Threads stdc++fs)

Option 2: Install OpenTimer

Our project CMakeLists.txt has defined the required files to install when you hit make install. The installation paths are <prefix>/include, <prefix>/lib, and <prefix>/bin for the hpp files, library, and executable where <prefix> can be configured through the cmake variable CMAKE_INSTALL_PREFIX. The following example installs OpenTimer to /tmp.

~$ cd build/
~$ cmake ../ -DCMAKE_INSTALL_PREFIX=/tmp
~$ make 
~$ make install
~$ cd /tmp            # install OpenTimer to /tmp
~$ ls
bin/  include/  lib/  # OpenTimer headers, libraries, and binaries

To build your application on top of the OpenTimer headers and library, you need -std=c++1z and -lstdc++fs flags to enable C++17 standard and filesystem libraries.

~$ g++ app.cpp -std=c++1z -O2 -I include -L lib -lOpenTimer -lpthread -o app.out "-lstdc++fs "
~$ ./app.out

OpenTimer C++ API

The class Timer is the main entry you need to call OpenTimer in your project. The table below summarizes a list of commonly used methods.

Method Type Argument Return Description
read_celllib builder path, split self read the cell library for early and late splits
read_verilog builder path self read a verilog netlist
read_spef builder path self read parasitics in SPEF
read_sdc builder path self read a Synopsys Design Constraint file
update_timing action void void update the timing; all timing values are up-to-date upon return
tns action void optional of float update the timing and return the total negative slack if exists
wns action void optional of float update the timing and return the worst negative slack if exists
dump_graph accessor ostream void dump the timing graph in DOT format to an output stream
dump_timer accessor ostream void dump the design statistics to an output stream

All public methods are thread-safe as a result of OpenTimer lineage.

timer.set_num_threads(n) sets the Taskflow worker count; zero requests one worker. The calling thread waits for each timing action. Without an explicit budget, the timer uses hardware concurrency (at least one worker). The pool is created on the first action and can be resized between actions without losing queued builders or incremental timing state. timer.num_threads() reports the configured worker count, or the actual pool size once created.

With one worker, timing propagation executes directly in topological candidate order, avoiding per-pin task and dependency allocations. Cones containing new or retained strongly connected components are reordered after the existing loop breakers are applied. Multiple workers keep the task-graph schedule. Builder execution and incremental candidate discovery are unchanged.

timer.update_parasitics(spef) applies an in-memory RC batch synchronously. It first materializes pending builders, replaces the affected RC trees and releases the SPEF payload. It retains all timing frontiers without propagating arrival, required time or slew; call update_timing() after the last batch. This lets placement hosts bound temporary RC storage while updating large designs. read_parasitics and read_spef retain their deferred behavior.

Pin timing provenance uses compact private corner/transition fields while keeping the public enum types and timing values unchanged. Arc objects do not reserve an internal-power array: that array was never populated, and path points continue to report zero internal power. Pin power reporting is unchanged. On the supported 64-bit build this reduces sizeof(Arc) from 264 to 200 bytes and sizeof(Pin) from 656 to 560 bytes.

RC-tree nodes use their owning map's labels instead of keeping another string per node. Adjacency stores indices into a contiguous edge vector, preserving insertion order across vector growth. Nodes retain only outgoing edges; the unused incoming lists and their per-edge allocations are omitted. Tree/SPEF exporters resolve those labels while writing the report; timing propagation does not allocate a second name table.

The example below shows an OpenTimer application and the use of builder, action, and accessor API.

#include <ot/timer/timer.hpp>                     // top-level header to include

int main(int argc, char *argv[]) {
  
  ot::Timer timer;                                // create a timer instance (thread-safe)
  
  timer.read_celllib("simple.lib", std::nullopt)  // read the library (O(1) builder)
       .read_verilog("simple.v")                  // read the verilog netlist (O(1) builder)
       .read_spef("simple.spef")                  // read the parasitics (O(1) builder)
       .read_sdc("simple.sdc")                    // read the design constraints (O(1) builder)
       .update_timing();                          // update timing (O(1) builder)

  if(auto tns = timer.report_tns(); tns) std::cout << "TNS: " << *tns << '\n';  // (O(N) action)
  if(auto wns = timer.report_wns(); wns) std::cout << "WNS: " << *wns << '\n';  // (O(N) action)
  
  timer.dump_timer(std::cout);                    // dump the timer details (O(1) accessor)
  
  return 0;
}

To see the full API list, visit OpenTimer Wiki.

Examples

The folder example contains several examples and is a great place to learn how to use OpenTimer.

Example Description Library
simple A timing report example on a sequential circuit design. OSU Free PDK 45nm
incremental An incremental timing example to create a sequential design from the ground up using OpenTimer's C++ API OSU Free PDK 45nm
unit A dimensional analysis example with different time unit conversions. OSU Free PDK 45nm
sizer A gate-sizing example on a design with six NAND2 cells. NanGate 45nm Open Cell Library
optimizer A timing-driven optimization example plus incremental timing update. TAU15 contest library

The folder benchmark contains more designs and they are mainly used for internal regression and integration tests.

Who is Using OpenTimer?

OpenTimer is an award-winning tools. It won ACM TAU Timing Analysis Contests multiple times (1st Place in 2014, 2nd Place in 2015, and 1st Place in 2016), the Special Price in the 2016 LibreCores Design Contest, and the Best Tool Winner in the 2018 WOSET at ICCAD. Many industry and academic people are using OpenTimer in their projects:

  • Golden Timer, 2019 ACM TAU Timing Analysis Contest on Timing-driven Optimization
  • Golden Timer, 2018 ACM TAU Timing Analysis Contest on Timing Reports from an STA Graph
  • Golden Timer, 2017 ACM TAU Timing Analysis Contest on Micro Modeling
  • Golden Timer, 2016 ACM TAU Timing Analysis Contest on Micro Modeling
  • Golden Timer, 2015 ACM/IEEE ICCAD Incremental Timing-driven Placement Contest
  • VSD: VLSI System Design Corporation
  • OpenDesign Flow Database: the infrastructure for VLSI design and design automation research
  • CloudV: a cloud-based platform to design and test chips for free
  • LGraph: live graph infrastructure for synthesis and simulation
  • Qflow: a digital synthesis flow using open-source EDA tools
  • Ophidian: an open-source library for physical design research and teaching

Please don't hesitate to let me know if I forgot your project!

Get Involved

Contributors & Acknowledgment

OpenTimer is being actively developed and contributed by the following people:

  • Tsung-Wei Huang created the OpenTimer project is now the chief architect.
  • Martin Wong supported the OpenTimer project through NSF and DARPA funding.
  • Andreas Olofsson supported the OpenTimer project through the DARPA IDEA project.
  • Chun-Xun Lin implemented the prompt interface of OpenTimer shell.
  • Kunal Ghosh provided a list of practical features to include in OpenTimer.
  • Pei-Yu Lee provided useful incremental timing discussion and helped fix bugs.
  • Tin-Yin Lai discussed micro modeling algorithms and helped fix bugs.
  • Jin Hu helped define the timing API and produced the golden benchmarks for integration tests.
  • Myung-Chul Kim helped test OpenTimer through ICCAD CAD contest.
  • George Chen helped defined path report formats and test OpenTimer through TAU contest.
  • Jignesh Shah contributed to the area and power report commands.
  • Pao-I Chen helped design the logo of OpenTimer.
  • Leslie Hwang reviewed the documentation and README.
  • Song Chen shared his opinion about command designs to avoid naming conflicts and litigation.
  • Che Chang designed parallelization strategies and performance optimizations to accelerate critical path generation.

Meanwhile, we appreciate the support from many organizations for our development of OpenTimer. Please don't hesitate to let me know if I forgot you!

License

OpenTimer is licensed under the MIT License:

Copyright © 2018 Dr. Tsung-Wei Huang and Dr. Martin Wong

The University of Illinois at Urbana-Champaign, IL, USA

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


The core of OpenTimer is under MIT license. However, it is built, tested, and documented using several third-party tools and services. Thanks a lot!

  • units: a compile-time header-only dimensional analysis and unit conversion
  • Parser-SPEF: a fast C++ header-only parser for standard parasitic exchange format (SPEF)
  • PEGTL: parsing expression grammar template library
  • Taskflow: fast C++ parallel programming with task dependencies
  • NanGate 45nm Library: open-source standard-cell library for testing and exploring EDA flows
  • OSU PDK: Oklahoma State University system on chip (SoC) design flows
  • Synopsys TAP-in: Synopsys technology access program for liberty user guide and open-source SDC parser.

You can find a copy of these licenses under the folder licenses.


Native RC timing model

The timer reduces an annotated RC tree to a driving-point Pi network using its first three admittance moments. Characterized cell drivers and SDC driving cells use a bounded effective-capacitance fit: the ramp/Thevenin driver matches Liberty timing and the charge drawn by that Pi network. This follows the Dartu, Menezes, Qian and Pillage gate-delay model, with an independently implemented scalar fit. Uncharacterized or failed fits keep full-load NLDM timing and Elmore wire delay, preserving the characterized transition (with sink-library threshold conversion). This includes constant-delay SRAM tables, which cannot supply a driver-resistance fit.

Wire delay and slew are voltage crossings of the driver/RC waveform, using library-specific rise/fall thresholds and slew derate. A single-pole step has 50-percent delay tau * ln(2) and 20-to-80-percent slew tau * ln(4); its Elmore time constant alone is not a voltage-threshold delay. Zero-RC nets retain normal Liberty timing. Physical load remains available through Net::load; Net::effective_load separately exposes the selected driver model's load.

RC annotations describe a particular connectivity. A pin insertion or removal invalidates that net's old RC tree, including old pin references. Reattach its parasitics after editing connectivity. Repowering with unchanged connectivity retains wire RC and recalculates the driver response. The model does not call OpenROAD or OpenSTA.

Single-pole threshold queries reuse the ramp ratio, tail coefficient and lazy logarithm within one wire response or fitting iteration. Each prepared ramp is local to that evaluation and keeps immutable input parameters; no mutable cache is shared across pins or threads. The scalar crossing API and solver arithmetic remain unchanged. This reduces repeated transcendental work without changing the circuit model.

Ramp fitting uses a reduced scalar equation to seed the duration when both characterized thresholds cross before the ramp ends. This removes repeated nested crossing solves for the initial estimate. The original bounded solver still validates the waveform interval at its existing tolerance; mixed/tail crossings retain its original seed. Long ramps already have an accurate linear estimate and skip the extra solve. Independent voltage-bisection regressions cover the ramp/tail boundary, feasibility limit and consistent time scales.

Each normal wire arc evaluates its response once per forward-propagation task: slew propagation also stores the response's delay for the immediately following delay pass. Arc delays are cleared before the pair runs. Ideal clocks bypass fanin slew propagation and retain their separate delay evaluation and masking; a missing source transition still supplies the existing zero-slew delay fallback without creating a sink slew. This reuse adds no persistent cache or per-arc storage. RC/model changes and ideal-clock/mask transitions are covered by unittest/rc_propagation.cpp.

Analytic waveform, Pi-circuit integration, shielding, failure fallback and incremental replacement checks are in unittest/rc_waveform.cpp and unittest/rc_ceff.cpp.

About

A High-performance Timing Analysis Tool for VLSI Systems

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages