OpenTimer: An Open-Source High-Performance Timing Analysis Tool

Authors: Tsung-Wei Huang and Martin D. F. Wong
Affiliation: Department of Electrical and Computer Engineering, University of Illinois at Urbana-Champaign (UIUC), IL, USA


Introduction

OpenTimer is a high-performance academic timing analysis tool developed by Tsung-Wei Huang and Prof. Martin D. F. Wong in the University of Illinois at Urbana-Champaign (UIUC), IL, USA. Evolving from its previous generation "UI-Timer", OpenTimer works on industry formats (.v, .spef, .lib, .sdc, .lef, .def), and supports important features such as block-based analysis, path-based analysis, cppr, incremental timing, and multi-threading. OpenTimer is extremely fast by its effective data structure and algorithm which can efficiently and accurately analyze large-scale designs. To further facilitate seamless integration between timing and other electronic design automation (EDA) applications such as timing-driven placement and routing, OpenTimer provides user-friendly application programming inteface (API) for interactive analysis. Most importantly, OpenTimer is open-source!

Figure 1: Program flowchart of OpenTimer.
Feature highlights:
  • Free and fully open-source under GPL.
  • Written in modern C++14 language.
  • Support for industry standard formats.
  • Built via Autotool for easy project maintenance.
  • Verified by industry IBM Einstimer.
  • Built-in regression for development integrity.
  • Multi-threading with portable OpenMP.
  • Pipeline parallelism for full/incremental timing.
  • Lazy evaluation for efficient incremental processing.
  • CPPR by default for less pessimism.
  • User-friendly APIs for drop-in integration.
  • Fast turnaround in large-scale design.

News

  • 2016/08/30: Source codes of OpenTimer 1.0.5 have been released! Please check the download section.
  • 2016/02/01: Source codes of OpenTimer 1.0.4 have been released! Please check the download section.
  • 2015/08/09: Source codes of previous generation, UI-Timer1.0 and UI-Timer2.0, have been released! Please check the download section.

Awards

  • First Place, ACM TAU Timing Contest on Macro Modeling, 2016 (TAU Contest 2016)
  • Second Place, ACM TAU Timing Contest on Incremental Timing and CPPR, 2015 (TAU Contest 2015)
  • First Place, ACM TAU Timing Contest on Common-Path-Pessimism Removal, 2014 (TAU Contest 2014)

Recognition

Experimental Results

OpenTimer is implemented in modern C++ language (by modern we mean C++14) with GCC 5.2 on a 2.20 GHz 64-bit Linux machine with 128 GB memory supporting 8 threads. The OpenMP APIs are used for our multi-threaded programming. Experiments are undertaken on a set of industry benchmarks released from TAU 2015 CAD contest. The golden reference is generated from IBM EinsTimer based on an operation set of timing queries and design modifiers. The following table and figures demonstrates the solution quality of OpenTimer in comparison to the top performers, iitRACE and iTimerC 2.0, in TAU 2015 CAD contest. Please notice that the accuracy margin is mainly caused by tie breaking to the golden reference.

Table I: Performance Comparison between OpenTimer and Top Performers iTimerC2.0 and iitRACE from 2015 TAU Timing Analysis Contest.
Circuit #Gates #Nets #OPs iitRACE iTimerC 2.0 OpenTimer
accuracy runtime memory accuracy runtime memory accuracy runtime memory
b19 255.3K 255.3K 5641.5K 63.03% 629s 3.0G 99.95% 215s 5.8G 99.95% 52s 4.6G
cordic 45.4K 45.4K 1607.6K 61.83% 100s 0.9G 98.88% 80s 1.3G 98.88% 18s 1.3G
des_perf 138.9K 139.1K 4326.7K 67.43% 299s 4.2G 97.02% 92s 3.1G 99.73% 30s 3.0G
edit_dist 147.6K 150.2K 3368.3K 64.83% 857s 2.0G 98.29% 98s 3.8G 98.30% 42s 3.8G
fft 38.2K 39.2K 1751.7K 89.66% 70s 0.5G 98.45% 49s 1.2G 99.77% 11s 1.2G
leon2 1614.4K 1517.0K 8438.5K 72.34% 16832s 9.9G 100.00% 787s 27.2G 100.00% 282s 22.8G
leon3mp 1247.7K 1248.0K 8405.9K 62.99% 4960s 8.2G 100.00% 609s 19.8G 100.00% 163s 17.9G
mgc_edit_dist 161.7K 164.2K 3403.4K 64.29% 1578s 1.9G 100.00% 135s 4.1G 100.00% 41s 3.1G
mgc_matrix_mult 171.3K 174.5K 3717.5K 67.93% 1363s 2.0G 100.00% 157s 4.3G 100.00% 31s 3.1G
netcard 1496.0K 1497.8K 11594.6K 87.63% 6662s 9.4G 99.99% 691s 22.9G 99.99% 192s 20.8G
cordic_core 3.6K 3.6K 226.0K 59.42% 21s 0.3G 95.19% 29s 0.2G 95.19 3s 0.1G
crc32d16N 478 495 28.9K 57.15% 3s 0.1G 100.00% 5s 0.1G 100.00% 1s 0.1G
softusb_navre 6.9K 7.0K 427.8K 40.17% 21s 0.1G crashed crashed crashed 99.97% 4s 0.5G
tip_master 37.7K 38.5K 1300.4K 82.95% 64s 0.6G 96.42% 47s 1.0G 97.04% 9s 0.8G
vga_lcd_1 139.5K 139.6K 2961.5K 99.65% 260s 1.6G 100.00% 94s 2.2G 100.00% 21s 2.9G
vga_lcd_2 259.1K 259.1K 12674.7K 98.57% 1132 13.3G 100.00% 156s 5.0G 100.00% 65s 3.9G

Download and License

The newest version of OpenTimer is 1.0.5 and it can be downloaded here OpenTimer-1.0.5.tar.gz. For those who are interested in its previous generations, UI-Timer 1.0 and UI-Timer 2.0, the source codes are available here and here. Our tools are free software subject to the terms of GNU general public license version 3 (GPL v3.0). Please do make sure you understand the content of GPL before any advanced distribution. If you are using our tools for publication, please cite our paper "OpenTimer: A High-Performance Timing Analysis Tool" in 2015 IEEE/ACM ICCAD, instead of this website.

Previous releases

Build and Regression

Building OpenTimer requires a GNU g++ compiler at least version 4.8 and GNU Autotool (autoconf, automake, and libtool). Download and decompress the OpenTimer distribution package. Enter the OpenTimer home directory and follow the standard autotool building process:
~$ ./configure
~$ make
~$ make install (Optional)
After the building process is successfully completed, executables and libraries are respectively placed in bin/ and lib under the OpenTimer home directory. By default, autotool generates a set of shared libraries and it can be reconfigured to static libraries through ./configure --disable-shared followed by make. Installation will copy the binaries, header files, and libraries into the directory /user/local or user-overridden position prefix=/permissible/path. For the later case, the environment variable LD_LIBRARY_PATH needs to be substituted appropriately so as to avoid linking error. To give a quick check of the building process, run the regression target as follows:
~$ make regression
Performing regression on TAU15 benchmarks ...
Regression on         ac97_ctrl: FINAL ACCURACY: Average of path and value is 100.00000
Regression on          aes_core: FINAL ACCURACY: Average of path and value is 100.00000
Regression on             c1355: FINAL ACCURACY: Average of path and value is 100.00000
...
Regression on         usb_funct: FINAL ACCURACY: Average of path and value is 100.00000
Regression on      usb_phy_ispd: FINAL ACCURACY: Average of path and value is 100.00000
Regression on            wb_dma: FINAL ACCURACY: Average of path and value is 100.00000
Regression runtime: 56 seconds
Regression runs through many benchmarks and compares the results with pre-generated golden reference. Ideally the final accuracy should be 100.00 for all benchmarks. Regression is a crucial step for those who aim to modify the source codes of OpenTimer. It should be run right after every major change in order to ensure such changes do not introduce software bugs and runtime faults. In addition to regression, monitoring the memory status is another important step in reliable software engineering. The memory control is written very carefully in OpenTimer. We have comprehensively examined the memory allocation and deallocation using famous memory tool valgrind to ensure zero memory leak. Below demonstrates the memory report running OpenTimer with valgrind on an example circuit.
~$ valgrind ./bin/OpenTimer < ./example/c17/c17.conf
==11195== Memcheck, a memory error detector
==11195== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==11195== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==11195== Command: ./bin/OpenTimer < ./example/c17/c17.conf
==11195==
...
==11195== HEAP SUMMARY:
==11195==     in use at exit: 2,241,719 bytes in 25 blocks
==11195==   total heap usage: 109,249 allocs, 109,224 frees, 151,041,999 bytes allocated
==11195== 
==11195== LEAK SUMMARY:
==11195==    definitely lost: 0 bytes in 0 blocks
==11195==    indirectly lost: 0 bytes in 0 blocks
==11195==      possibly lost: 2,240 bytes in 7 blocks
==11195==    still reachable: 2,239,479 bytes in 18 blocks
==11195==         suppressed: 0 bytes in 0 blocks
==11195== Rerun with --leak-check=full to see details of leaked memory
==11195== 
==11195== For counts of detected and suppressed errors, rerun with: -v
==11195== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Interactive Analysis with OpenTimer Shell

OpenTimer's shell provides a simple way to learn the API, as well as a powerful tool to analyze the design interactively. Build OpenTimer first and run the binary bin/OpenTimer. A welcome message showing the tool information will be displayed followed by the interactive command prompt.
~$ ./bin/OpenTimer
  ____              _______              
 / __ \___  ___ ___/_  __(_)_ _  ___ ____
/ /_/ / _ \/ -_) _ \/ / / /  ' \/ -_) __/
\____/ .__/\__/_//_/_/ /_/_/_/_/\__/_/       version 1.0.4
    /_/                                     
Copyright (c) 2015, Tsung-Wei Huang and Martin D. F. Wong,
University of Illinois at Urbana-Champaign (UIUC), IL, USA.
All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
For bug reporting instructions and manual for OpenTimer, please see:
<http://web.engr.illinois.edu/~thuang19>
For help, type "help".
OpenTime>
We have provided several example designs and configuration files in the folder example/ to familiarize users with the command usage. For a rough overview of the API, start with the simple design example/simple and feed the configuration file input into OpenTimer shell in a standard input manner:
~$ cd example/simple/
~$ cat simple.conf
set_early_celllib_fpath simple_Early.lib
set_late_celllib_fpath simple_Late.lib
set_spef_fpath simple.spef
set_verilog_fpath simple.v
set_timing_fpath simple.timing
init_timer
...
report_at -pin out -late -rise
report_worst_paths -pin out
report_tns
report_wns
~$ ../../bin/OpenTimer < simple.conf
I0209 23:18:44.633735 28065 ot_environment.h:140] Setting 1 thread(s)
I0209 23:18:44.633874 28065 ot_environment.h:104] Setting /OpenTimer-1.0.4/example/simple/simple.v
I0209 23:18:44.633898 28065 ot_environment.h:116] Setting /OpenTimer-1.0.4/example/simple/simple_Early.lib
I0209 23:18:44.633913 28065 ot_environment.h:122] Setting /OpenTimer-1.0.4/example/simple/simple_Late.lib
I0209 23:18:44.633929 28065 ot_environment.h:128] Setting /OpenTimer-1.0.4/example/simple/simple.timing
I0209 23:18:44.633944 28065 ot_environment.h:110] Setting /OpenTimer-1.0.4/example/simple/simple.spef
I0209 23:18:44.633955 28065 ot_timer.cpp:3316] Initializing timer ...
I0209 23:18:44.665405 28065 ot_verilog.cpp:42] Loading /OpenTimer-1.0.4/example/simple/simple.v
I0209 23:18:44.682214 28065 ot_celllib.cpp:147] Loading /OpenTimer-1.0.4/example/simple/simple_Late.lib
I0209 23:18:44.791131 28065 ot_celllib.cpp:147] Loading /OpenTimer-1.0.4/example/simple/simple_Early.lib
I0209 23:18:44.877457 28065 ot_timer.cpp:542] Loading /OpenTimer-1.0.4/example/simple/simple.timing
I0209 23:18:44.886569 28065 ot_timer.cpp:3407] Successfully initialized the timer
...
report_worst_paths 1
Path 1: RAT -130.921 8 L
out R
u3:o R
u3:a F
u2:o F
u2:a R
f1:q R
f1:ck R
iccad_clk R
...
Look into the configuration file simple.conf for an in-depth overview of each command or type help in the shell prompt to get a summary over different categories. Comprehensive understanding of command details can be referred to the source codes, in particular, src/Timer/ot_timer.cpp and src/Shell/ot_shell.cpp. Here we summarize a list of commonly used commands:

Command type Command name Command Argument Function
design-initiate set_early_celllib_fpath <.lib> Set the file path of the early cell library.
set_late_celllib_fpath <.lib> Set the file path of the late cell library.
set_verilog_fpath <.v> Set the file path of the flat verilog module.
set_spef_fpath <.spef> Set the file path of the initial spef file.
set_timing_fpath <.timing> Set the file path of the initial timing assertion file (see TAU 2015 CAD contest file format).
set_num_threads <int> Set the number of threads for timer.
set_at -pin <pin_name> -early/-late -rise/-fall value Assert a new arrival time value to a primary input.
set_slew -pin <pin_name> -early/-late -rise/-fall value Assert a new slew value to a primary input.
set_load -pin <pin_name> -early/-late -rise/-fall value Assert a new load value to a primary output.
set_rat -pin <pin_name> -early/-late -rise/-fall value Assert a new required arrival time value to a primary output.
design-modifier insert_gate <gate_name> <cell_name> Insert a gate into the current design.
remove_gate <gate_name> Remove a disconnected gate from the current design.
repower_gate <gate_name> <cell_name> Repower an existing gate to another cell.
insert_net <net_name> Insert a new empty net into the current design.
remove_net <net_name> Remove an existing empty net from the current design.
disconnect_pin <pin_name> Disconnect an existing pin from its net.
connect_pin <pin_name> <net_name> Connect an existing pin to a given net.
read_spef <.spef> Assert a new parasitic file to the current design.
design-report report_worst_paths -numPaths <int> [-pin <pin_name>] Report the top -numPaths worst paths passing through the pin <pin_name> in the design. If <pin_name> is not specified, the worst path is across the global design.
report_at -pin <pin_name> -early/-late -rise/-fall Report the arrival time at the pin <pin_name> at a given early-late split and rise-fall transition.
report_slew -pin <pin_name> -early/-late -rise/-fall Report the slew at the pin <pin_name> at a given early-late split and rise-fall transition.
report_rat -pin <pin_name> -early/-late -rise/-fall Report the required arrival time the pin <pin_name> at the given early-late split and rise-fall transition.
report_slack -pin <pin_name> -early/-late -rise/-fall Report the slack at the pin <pin_name> at the given early-late split and rise-fall transition.
report_tns Report the total negative slack.
report_wns Report the worst negative slack.
design-exec exec_ops <.ops> Execute the given .ops file (see TAU 2015 CAD contest file format).
init_timer Initialize the timer. This should be called after the design had been initiated appropriately.
update_timing <bool = true> Update the timing. True for incremental update and False for full timing.

Integrate OpenTimer into Your Project

OpenTimer offers drop-in integration into your project. This is especially useful for the development of timing-driven applications such as incremental timing-driven placement/routing and timing optimization. The most common way is, as the autotool convention, to install OpenTimer using make install and link required header files and libraries to your applications. Or you can directly create codes in src/ directory and modify Makefile.am accordingly to include any new source files into the automake process. An example of OpenTimer-based application can be referred to example/simple/simple.cpp.
~$ cat example/simple/simple.cpp

#include "ot_timer.h"

// Function: main
int main(int argc, char *argv[]) {
  
  using namespace OpenTimer;

  // Usage of OpenTimer.
  Timer::init_logging(argv[0], 1);  // Logging the program to stderr.
  Timer timer;                      // Create a timer object.
  
  // Initiate the design. At minimum, it requires one .v, two .lib (early and late),
  // one .timing, and one .spef.
  timer.set_num_threads(8); 
  timer.set_verilog_fpath("simple.v");
  timer.set_early_celllib_fpath("simple_Early.lib");
  timer.set_late_celllib_fpath("simple_Late.lib");
  timer.set_timing_fpath("simple.timing");
  timer.set_spef_fpath("simple.spef");

  // Initialize the timer. This should be callsed after the design had been
  // appropriately initiated.
  timer.init_timer();

  // Example: Report the TNS and WNS for the design.
  printf("TNS = %.4f, WNS = %.4f\n", timer.tns(), timer.wns());
  
  // Example: Run design modifiers.
  timer.repower_gate("u3", "INV_X1");
  timer.insert_gate("newbox", "INV_X3");
  timer.insert_net("newnet");
  timer.disconnect_pin("u3:o");
  timer.connect_pin("newbox:o", "out");
  timer.connect_pin("u3:o", "newnet");
  timer.connect_pin("newbox:a", "newnet");
  timer.read_spef("change1.spef");
  timer.repower_gate("u3", "INV_X2");
  timer.disconnect_pin("newbox:a");
  timer.disconnect_pin("newbox:o");
  timer.disconnect_pin("u3:o");
  timer.remove_net("newnet");
  timer.remove_gate("newbox");
  timer.connect_pin("u3:o", "out");
  timer.read_spef("change2.spef");

  // Example: Query the timing for a pin at any transition and split.
  printf("LATE/FALL slack at pin 'out' = %.4f\n", timer.slack("out", LATE, FALL)); 
  printf("LATE/RISE arrival time at pin 'out' = %.4f\n", timer.at("out", LATE, RISE)); 
  printf("EALRY/FALL required arrival time at pin 'out' = %.4f\n", timer.rat("out", EARLY, FALL)); 

  // Example: Report the top two critical paths in the design. 
  // The path trace is stored in the class object 'Path' which has been typedefed as follows:
  // In ot_typedef.h
  path_cache_t cache;
  printf("Top-two critical paths in the design.\n");
  timer.get_worst_paths("", 2, cache);
  for(unsigned i=0; i<cache.path_ptrs().size(); ++i) {
    path_rt p = *(cache.path_ptrs()[i]);
    switch(p.path_type()) {
      case RAT_PATH_TYPE:
        printf("Path %d: RAT %.3f %lu %c\n", i, p.slack(), p.size(), (p.el() ? 'L' : 'E'));
      break;
      case SETUP_PATH_TYPE:
        printf("Path %d: Setup %.3f %lu %c\n", i, p.slack(), p.size(), (p.el() ? 'L' : 'E'));
      break;
      case HOLD_PATH_TYPE:
        printf("Path %d: Hold %.3f %lu %c\n", i, p.slack(), p.size(), (p.el() ? 'L' : 'E'));
      break;
      default:
        assert(false);
      break;
    }
    for(PathIter i(p); i(); ++i) {
      printf("%s %c\n", i.pin_ptr()->name().c_str(), i.rf() ? 'F' : 'R');
    }
  }
  return 0;
}
The OpenTimer source codes are strictly namespace-wrapped. The identifier OpenTimer::Timer is one of the major classes that hook up the entire timing engine and provide a set of API methods. Detailed definitions can be referred to src/Timer/ot_timer.cpp and src/Timer/ot_timer.h. Before OpenTimer starts, the input design must be appropriately initiated. A minimum input set requires two cell library files (.lib) respectively for early and late timing split, one flat gate-level verilog file (.v), one parasitics file (.spef), and one timing assertion file (.timing or .sdc). Use the design-initiate methods (e.g., set_verilog_fpath, set_early_celllib_fpath, etc) to set up the design and then call init_timer to initialize the timer. Soon after the timer is initialized, we can start running a variety of function calls and procedures such as repower_gate, connect_pin, read_spef, tns, and so on.

Reference

  1. T.-W. Huang and Martin D. F. Wong, "OpenTimer: A High-Performance Timing Analysis Tool", Proceedings of IEEE/ACM International Conference on Computer-Aided Design (ICCAD), pp. 895-902, Austin, TX, 2015.
  2. T.-W. Huang, P.-C. Wu, and Martin D. F. Wong, "UI-Timer: An Ultra-Fast Clock Network Pessimism Removal Algorithm", Proceedings of IEEE/ACM International Conference on Computer-Aided Design (ICCAD) (slide), pp. 758-765, San Jose, CA, 2014.

Acknowledgment

  • Jin Hu, IBM Corporation, NY, USA
  • Myung-Chul Kim, IBM Corporation, TX, USA
  • Pei-Yu Lee, National Chiao Tung University, Hsinchu, Taiwan

Bug Report and Maintenance

OpenTimer is actively maintained by Tsung-Wei Huang. If you are discovering any bugs or are interested in pursuing an OpenTimer-based project with me, please contact me at twh760812@gmail.com.
Last updated 03/03/2017