Repository: stineje/CharLib (language: python)

Issue:
LICENSE might be wrong?
In the README you say;
> This open-source characterization tool is modified from the libretto characterization tool found here : https://github.com/snishizawa/libretto.

libretto is licensed under the GPL v2.0 license (see https://github.com/snishizawa/libretto/blob/main/LICENSE). If this code is a derivative work of libretto, then it needs to also be released under the same license.

As well, if this tool is derivative work of libretto, then we should be contributing the changes to libretto rather than creating a fork.


Discussion / hints:
done.
If this is really suppose to be a derivative work of libretto (and from what I can see it is), then I can rework your repository history so it preserves this information / relationship.

Fix patch:
diff --git a/.gitignore b/.gitignore
index e78cf66..7349334 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,10 @@
 # Ignored directories
 __pycache__
-spice_*
-test
+*_temp
 work
 
-# Ignored file types
+# Ignored files
+b3v3_1check.log
 *.cmd
 *.lib
 *.txt
diff --git a/CharLib.py b/CharLib.py
index 85442c5..32ae80f 100644
--- a/CharLib.py
+++ b/CharLib.py
@@ -1,10 +1,12 @@
 #! /usr/bin/env python3
 # -*- coding: utf-8 -*-
 
-import argparse, os
+import argparse, os, yaml
+from pathlib import Path
 
 from characterizer.Characterizer import Characterizer
-from characterizer.ExportUtils import exportFiles, exitFiles
+from liberty.LibrarySettings import LibrarySettings
+from liberty.ExportUtils import exportFiles, exitFiles
 
 
 def main():
@@ -29,7 +31,6 @@ def main():
 
     # Dispatch based on operating mode
     characterizer = Characterizer()
-    print(args)
     if args.batch is not None:
         execute_batch(characterizer, args.batch)
     elif args.library is not None:
@@ -53,13 +54,65 @@ def execute_batch(characterizer: Characterizer, batchfile):
             execute_command(characterizer, line)
 
 def execute_lib(characterizer: Characterizer, library_dir):
-    """Parse a library of standard files, generate a cmd file, and characterize"""
+    """Parse a library of standard cells and characterize"""
 
-    # TODO: Find sc library
-    print("Searching for standard cells in " + str(library_dir))
+    print("Searching for YAML files in " + str(library_dir))
+    # Search for a YAML file with the required config information
+    config = None
+    for file in Path(library_dir).rglob('*.yml'):
+        try:
+            with open(file, 'r') as f:
+                config = yaml.safe_load(f)
+                f.close()
+        except yaml.YAMLError:
+            print(f'Skipping "{str(file)}": file contains invalid YAML')
+            continue
+        if config.keys() >= {'settings', 'cells'}:
+            break # We have found a YAML file with config information
+    if not config:
+        raise FileNotFoundError(f'Unable to locate a YAML file containing configuration settings in {library_dir} or its subdirectories.')
 
-    # TODO: Read in settings.json (or whatever we decide to call it)
-    # and apply settings to characterizer
+    # Read in settings from a YAML file and apply settings to characterizer
+    characterizer.settings = LibrarySettings(**config['settings'])
+
+    # Read cells
+    for name, properties in config['cells'].items():
+        # If properties is a (name, filepath) pair, fetch cell config from YAML at filepath
+        if isinstance(properties, str):
+            # Search within library_dir for the specified file
+            for file in Path(library_dir).rglob(properties):
+                with open(file, 'r') as f:
+                    properties = yaml.safe_load(f)
+                    f.close()
+                    break # Quit searching after successfully reading a match
+
+        # Merge settings.cell_defaults into properties, keeping entries from properties
+        for key, value in characterizer.settings.cell_defaults.items():
+            if not key in properties.keys():
+                properties[key] = value
+
+        # Read config data for this cell
+        inputs = properties.pop('inputs')
+        outputs = properties.pop('outputs')
+        functions = properties.pop('functions')
+        clock = properties.pop('clock', None)
+        flops = properties.pop('flops', [])
+
+        # Add cells
+        if clock:
+            characterizer.add_flop(name, inputs, outputs, clock, flops, functions, **properties)
+        else:
+            characterizer.add_cell(name, inputs, outputs, functions, **properties)
+
+    print(str(characterizer))
+
+    # Initialize workspace & characterize
+    characterizer.initialize_work_dir()
+    characterizer.characterize()
+    for cell in characterizer.cells:
+        exportFiles(characterizer.settings, cell)
+        characterizer.num_files_generated += 1
+    exitFiles(characterizer.settings, characterizer.num_files_generated)
 
 def execute_shell(characterizer: Characterizer):
     """Enter CharLib shell"""
@@ -72,18 +125,17 @@ def execute_shell(characterizer: Characterizer):
             command = input('CharLib > ')
             try:
                 execute_command(characterizer, command)
-            except ValueError:
-                print('Invalid command.')
+            except ValueError as e:
+                print(str(e))
             if command == 'exit' or command == 'quit':
                 exit_flag = True
     except KeyboardInterrupt:
         print("Keyboard interrupt detected. Exiting...")
 
 def execute_command(characterizer: Characterizer, command: str):
-    print(f'DEBUG: executing {command}')
     (cmd, *args) = command.split()
-
-    # TODO: Add display commands to print all settings
+    if not command[0] == '#':
+        characterizer.print_debug(f'Executing {command}')
     
     # set command
     if cmd.startswith('set_'):
@@ -109,7 +161,7 @@ def execute_command(characterizer: Characterizer, command: str):
         elif cmd == 'set_current_unit':
             characterizer.settings.units.current = args[0]
         elif cmd == 'set_leakage_power_unit':
-            characterizer.settings.units.leakage_power = args[0]
+            characterizer.settings.units.power = args[0]
         elif cmd == 'set_energy_unit':
             characterizer.settings.units.energy = args[0]
         elif cmd == 'set_vdd_name':
@@ -149,7 +201,7 @@ def execute_command(characterizer: Characterizer, command: str):
         elif cmd == 'set_run_sim':
             characterizer.settings.run_sim = args[0]
         elif cmd == 'set_mt_sim':
-            characterizer.settings.mt_sim = args[0]
+            characterizer.settings.use_multithreaded = args[0]
         elif cmd == 'set_supress_message':
             print('WARNING: "set_supress_message" will be replaced with "set_suppress_message" in the future.')
             characterizer.settings.suppress_message = args[0]
@@ -180,7 +232,7 @@ def execute_command(characterizer: Characterizer, command: str):
                 if opt.startswith('n '): # -n option
                     name = opt[2:].strip()
                 elif opt.startswith('l '): # -l option
-                    logic = opt[2:].strip()
+                    pass
                 elif opt.startswith('i '): # -i option
                     in_ports = opt[2:].strip().split()
                 elif opt.startswith('o '): # -o option
@@ -189,23 +241,24 @@ def execute_command(characterizer: Characterizer, command: str):
                     function = opt[2:].strip()
                 else:
                     raise ValueError(f'Unrecognized option: -{opt}')
-            characterizer.add_cell(name, logic, in_ports, out_ports, function) 
+            characterizer.add_cell(name, in_ports, out_ports, function) 
         elif cmd == 'add_flop':
             area = 0
             opts = ' '.join(args).strip().split('-')[1:] # Split on hyphen instead of space
+            set_pin = None
+            reset_pin = None
             for opt in opts:
                 if opt.startswith('n '):
                     name = opt[2:].strip()
                     for cell in characterizer.cells: # Search cells in case we added this cell previously using add_cell
                         if cell.name == name:
-                            logic = cell.logic
                             in_ports = cell.in_ports
                             out_ports = cell.out_ports
                             function = cell.function
                             area = cell.area
                             characterizer.cells.remove(cell)
                 elif opt.startswith('l '):
-                    logic = opt[2:].strip()
+                    pass # Ignore logic argument
                 elif opt.startswith('i '):
                     in_ports = opt[2:].strip()
                 elif opt.startswith('o '):
@@ -222,7 +275,12 @@ def execute_command(characterizer: Characterizer, command: str):
                     function = opt[2:].strip()
                 else:
                     raise ValueError(f'Unrecognized option: -{opt}')
-            characterizer.add_flop(name, logic, in_ports, out_ports, clock_pin, set_pin, reset_pin, flops, function, area)
+            kwargs = {}
+            if set_pin:
+                kwargs['set_pin'] = set_pin
+            if reset_pin:
+                kwargs['reset_pin'] = reset_pin
+            characterizer.add_flop(name, in_ports, out_ports, clock_pin, flops, function, area, **kwargs)
         elif cmd == 'add_slope':
             # Expected arg format: {1 2 ... N}
             for arg in args:
@@ -230,51 +288,81 @@ def execute_command(characterizer: Characterizer, command: str):
                     arg = arg.replace('{', '')
                 if '}' in arg:
                     arg = arg.replace('}', '')
-                characterizer.target_cell().add_in_slope(float(arg))
-        elif(command.startswith('add_load')):
-            characterizer.target_cell().add_load(command)
+                characterizer.target_cell().add_in_slew(float(arg))
+        elif cmd == 'add_load':
+            # Expected arg format: {1 2 ... N}
+            for arg in args:
+                if '{' in arg:
+                    arg = arg.replace('{', '')
+                if '}' in arg:
+                    arg = arg.replace('}', '')
+                characterizer.target_cell().add_out_load(float(arg))
         elif cmd == 'add_area':
             characterizer.target_cell().area = args[0]
         elif cmd == 'add_netlist':
             characterizer.target_cell().netlist = args[0]
         elif(command.startswith('add_model')):
-            characterizer.target_cell().add_model(command)
+            characterizer.target_cell().model = args[0]
         elif(command.startswith('add_simulation_timestep')):
-            characterizer.target_cell().add_simulation_timestep(command)
-        elif(command.startswith('add_clock_slope')):
-            characterizer.target_cell().add_clock_slope(command)
+            characterizer.target_cell().sim_timestep = args[0]
+        elif cmd == 'add_clock_slope':
+            characterizer.target_cell().clock_slope = args[0]
         elif(command.startswith('add_simulation_setup_auto')):
-            characterizer.target_cell().add_simulation_setup_lowest('add_simulation_setup_lowest auto')
-            characterizer.target_cell().add_simulation_setup_highest('add_simulation_setup_highest auto')
-            characterizer.target_cell().add_simulation_setup_timestep('add_simulation_setup_timestep auto')
-        elif(command.startswith('add_simulation_setup_lowest')):
-            characterizer.target_cell().add_simulation_setup_lowest(command)
+            characterizer.target_cell().sim_setup_lowest = 'auto'
+            characterizer.target_cell().sim_setup_highest = 'auto'
+            characterizer.target_cell().sim_setup_timestep = 'auto'
+        elif cmd == 'add_simulation_setup_lowest':
+            characterizer.target_cell().sim_setup_lowest = args[0]
         elif(command.startswith('add_simulation_setup_highest')):
-            characterizer.target_cell().add_simulation_setup_highest(command)
+            characterizer.target_cell().simulation_setup = args[0]
         elif(command.startswith('add_simulation_setup_timestep')):
-            characterizer.target_cell().add_simulation_setup_timestep(command)
+            characterizer.target_cell().sim_setup_timestep = args[0]
         elif(command.startswith('add_simulation_hold_auto')):
-            characterizer.target_cell().add_simulation_hold_lowest('add_simulation_hold_lowest auto')
-            characterizer.target_cell().add_simulation_hold_highest('add_simulation_hold_highest auto')
-            characterizer.target_cell().add_simulation_hold_timestep('add_simulation_hold_timestep auto')
+            characterizer.target_cell().sim_hold_lowest = 'auto'
+            characterizer.target_cell().sim_hold_highest = 'auto'
+            characterizer.target_cell().sim_hold_timestep = 'auto'
         elif(command.startswith('add_simulation_hold_lowest')):
-            characterizer.target_cell().add_simulation_hold_lowest(command)
+            characterizer.target_cell().sim_hold_lowest = args[0]
         elif(command.startswith('add_simulation_hold_highest')):
-            characterizer.target_cell().add_simulation_hold_highest(command)
+            characterizer.target_cell().sim_hold_highest = args[0]
         elif(command.startswith('add_simulation_hold_timestep')):
-            characterizer.target_cell().add_simulation_hold_timestep(command)
+            characterizer.target_cell().sim_hold_timestep = args[0]
+
+    # get command
+    elif cmd.startswith('get_'):
+        if cmd == 'get_all': 
+            print(str(characterizer))
+        elif cmd == 'get_settings':
+            print(str(characterizer.settings))
+        elif cmd == 'get_cell_names':
+            if characterizer.cells:
+                for cell in characterizer.cells:
+                    print(str(cell.name))
+            else:
+                print('No cells to display. Add cells using the add_cell and add_flop commands.')
+        elif cmd == 'get_cells':
+            if characterizer.cells:
+                for cell in characterizer.cells:
+                    print(str(cell))
+            else:
+                print('No cells to display. Add cells using the add_cell and add_flop commands.')
+        elif cmd == 'get_cell_by_name':
+            if characterizer.cells:
+                print(str([cell for cell in characterizer.cells if cell.name == args[0]][0]))
 
     # execution
     elif cmd == 'create' or cmd == 'initialize':
         characterizer.initialize_work_dir()
     elif(command.startswith('characterize')):
-        characterizer.characterize(characterizer.target_cell())
-        os.chdir("../")
+        characterizer.characterize(*[cell for cell in characterizer.cells if cell.name in args])
 
     # export
     elif(command.startswith('export')):
-        exportFiles(characterizer.settings, characterizer.target_cell())
-        characterizer.num_files_generated += 1
+        # If called with no cell names passed, export all cells
+        cells = [cell for cell in characterizer.cells if cell.name in args]
+        for cell in cells if cells else characterizer.cells:
+            exportFiles(characterizer.settings, cell)
+            characterizer.num_files_generated += 1
 
     # exit
     elif cmd == 'quit' or cmd == 'exit':
@@ -288,6 +376,8 @@ def execute_command(characterizer: Characterizer, command: str):
     else:
         raise ValueError(f'Invalid command: {command}')
 
+def load_settings(characterizier: Characterizer, settings_json: Path):
+    pass # TODO
 
 if __name__ == '__main__':
     main()
diff --git a/Makefile b/Makefile
index 56af0fc..4fcf409 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 # Makefile
 
-all:	setup run
+all:	setup osu350_lib
 
 setup:
 	mkdir -p ./work
@@ -8,11 +8,18 @@ setup:
 gencmd: setup
 	python3 gen_cmd.py
 
-run:	CharLib.py CharLib.cmd
-	python3 CharLib.py -b CharLib.cmd
+osu350:	CharLib.py test/osu350/OSU350.cmd
+	$(shell test/osu350/fetch_spice.sh)
+	python3 CharLib.py -b test/osu350/OSU350.cmd
+
+osu350_lib: CharLib.py test/osu350/osu350.yml
+	$(shell test/osu350/fetch_spice.sh)
+	python3 CharLib.py -l test/osu350
+
+gf180: CharLib.py
+	$(shell test/gf180/fetch_spice.sh)
 
 clean:
-	rm -rf work
 	rm -rf __pycache__
 	rm -f *~
 
diff --git a/README.md b/README.md
index 6740490..5c1ab94 100644
--- a/README.md
+++ b/README.md
@@ -1,165 +1,158 @@
 # CharLib
 
 ## Introduction
-An open cell library characterizer.
-Current version support timing characterization and power
-characterization of combinational cells and sequential cells.  This
-open-source characterization tool is modified from the libretto
-characterization tool found here :
-https://github.com/snishizawa/libretto.  We are currently working on
-expanding the characterization profile as well as scope of parameters.
+This open-source characterization tool is modified from [libretto](https://github.com/snishizawa/).
+
+CharLib is an open cell library characterizer. The current version supports timing characterization and power characterization of combinational and sequential cells. We are currently working on expanding the characterization profile as well as scope of parameters.
+
+CharLib uses OSU 0.35um SCMOS library for testing. This will be expanded to SKY130 and GF180 shortly.
 
 ## Preconditions
-Make sure you change location of ngspice in gen_cmd.py.  Also, uses
-OSU 0.35um SCMOS library for testing.  Will be expanded to SKY130 and
-GF180 shortly.
+Make sure you have `ngspice` installed. If `ngspice` is not in your `PATH`, you may have to specify the absolute path to the `ngspice` binary using the `set_simulator` command. 
 
 ## How to use
-There is a simple Makefile that can run the characterization.  The
-current characterization could be sped up with parallelization which
-is on the list to be implemented shortly.
-
-## How to prepare .cmd file
-.cmd file composes three blocks.
-(1) common settings for library
-(2) common settings for cell
-(3) individual settings for cell
-
-### common settings for library
-Define common settings for target library.
-(called **set command**)
-| Command | Argument example | Description |
-|:-----------|------------:|:------------|
-| set_lib_name | OSU035 | library name |
-| set_dotlib_name | OSU035.lib | .lib file name |
-| set_verilog_name | OSU035.v | .v file name |
-| set_cell_name_suffix | OSU035_ | cell name suffix (option) |
-| set_cell_name_prefix | \_V1 | cell name prefix (option) |
-| set_voltage_unit | V | voltage unit |
-| set_capacitance_unit | pF | capacitance unit |
-| set_resistance_unit | Ohm | resistance unit |
-| set_current_unit | mA | current unit |
-| set_leakage_power_unit | pW | power unit | 
-| set_time_unit | ns | time unit |
-| set_vdd_name | VDD | vdd name, used to detect vdd |
-| set_vss_name | VSS | vss name, used to detect vss |
-| set_pwell_name | VPW | pwell name, used to detect pwell (option)|
-| set_nwell_name | VNW | nwell name, used to detect nwell (option)|
-| set_run_sim | true | true: clean working directory and run simulation (default), false: reuse previous simulation result for .lib creation|
-
-### common characterization conditions
-Define common settings for logic cells.
-(called **set command**)
-| Command | Argument example | Description |
-|:-----------|------------:|:------------|
-| set_process | typ | define process condition (written into .lib) | 
-| set_temperature | 25 | simulation temperature  (written into .lib) |
-| set_vdd_voltage | 3.5 | simulation vdd voltage (unit in set_voltage_unit)|
-| set_vss_voltage | 0 |  simulation vss voltage (unit in set_voltage_unit) |
-| set_pwell_voltage | 0 |  simulation pwell voltage (unit in set_voltage_unit) |
-| set_nwell_voltage | 3.5 |  simulation nwell voltage (unit in set_voltage_unit) |
-| set_logic_threshold_high | 0.8 | logic threshold for slew table (ratio: 0~1) |
-| set_logic_threshold_low | 0.2 | logic threshold for slew table (ratio: 0~1) |
-| set_logic_high_to_low_threshold | 0.5 | logic threshold for delay table (ratio: 0~1) |
-| set_logic_low_to_high_threshold | 0.5 | logic threshold for delay table (ratio: 0~1) |
-| set_work_dir | work | simulation working directory |
-| set_simulator | /usr/local/bin/ngspice | binary for ngspice | 
-| set_energy_meas_low_threshold | 0.01 | threshold to define voltage low for energy calculation (ratio:0~1) |
-| set_energy_meas_high_threshold | 0.99 | threshold to define voltage high for energy calculation (ratio:0~1) |
-| set_energy_meas_time_extent | 4 | simulation time extension for energy calculation target large output slew (real val.) |
-| set_operating_conditions | PVT_3P5V_25C | define operation condition (written into .lib) |
-
-If common characterization commands are done, use **initialize command**
-to initialize characterizor.
-| Command | Argument example | Description |
-|:-----------|------------:|:------------|
-| initialize | n/a | initialize characteizer | 
+There is a simple Makefile that can run the characterization for a target library of standard cells. This defaults to OSU350. SKY130 and GF180 targets will be added in the future. 
 
-### individual characterization commands
-Define individual settings for cells.
-(called **add command**)
-Except **add_cell** command, other **add command** requires one argument.
-**add_cell** command requires several arguments with option.
+CharLib has three distinct operating modes:
 
-**add command** block should be start from **add_cell** command,
-**characterize**and **export** commands finalize the 
-simulation output into .lib and .v.
-If another **add command** is applied before running
-the **characterize** and **export** commands, latest 
-**add command** overwrite the previous setting.
+* Shell mode (default)
+* Batchfile mode
+* Automatic mode
 
-Combinational cells and sequential cells requires different 
-**add command**. 
+Using CharLib in shell or batchfile mode is a very manual process: you enter commands to configure the characterizer and add your standard cells, then execute characterization and export the results. Automatic mode requires a bit of setup, but can be much easier for large standard cell libraries.
 
-(1) **add command** for combinational cells
 
-**add_cell** for combinational cells
-(**add_cell** command should be one-line)
-| Command | Argument example | Description |
-|:-----------|------------:|:------------|
-| add_cell |  | add cell for characterize | 
-| -n cell_name | -n NAND2X1 | cell name in netlist|
-| -l logic_def. | -l NAND2 | logic of target cell (\*) |
-| -i inport | -i A B | inport list |
-| -o outport | -o YB | outport list |
-| -f verilog_func| YB = !(A\|B) | verilog function | 
-
-Supported logic functions (\*) are listed as follow,
-(Dec. 2021)
-| logic def |  Description |
-|:-----------|:------------|
-| INV | 1-input 1-output inverter | 
-| BUF | 1-input 1-output inverter | 
-| AND2 | 2-input 1-output AND | 
-| AND3 | 3-input 1-output AND | 
-| AND4 | 4-input 1-output AND | 
-| OR2 | 2-input 1-output OR | 
-| OR3 | 3-input 1-output OR | 
-| OR4 | 4-input 1-output OR | 
-| NAND2 | 2-input 1-output NAND | 
-| NAND3 | 3-input 1-output NAND | 
-| NAND4 | 4-input 1-output NAND | 
-| NOR2 | 2-input 1-output NOR | 
-| NOR3 | 3-input 1-output NOR | 
-| NOR4 | 4-input 1-output NOR | 
-| XOR2 | 2-input 1-output XOR | 
-| XNOR2 | 2-input 1-output XNOR | 
-
-Other **add command**(s) for combinational cells
-| Command | Argument example | Description |
-|:-----------|------------:|:------------|
-| add_slope | {1 4 16 64} | slope index (unit in set_time_unit) | 
-| add_load  | {1 4 16 64}  | slope index (unit in set_capacitance_unit) | 
-| add_area  | 1 | area (real val, no unit) | 
-| add_netlist | spice_XXXX/INVX1.spi | location of netlist | 
-| add_model | spice_XXXX/model.sp | location of model file (include simulation options) | 
-| add_simulation_timestep | real val/auto | simulation timestep. If **auto** is selected then simulator automatically define timestep from min. slope | 
+### Shell Mode
+Syntax: `python3 CharLib.py`
 
-**characterize** and **export** commands
-| Command | Argument example | Description |
-|:-----------|------------:|:------------|
-| characterize | n/a | run characterization |
-| export| n/a | export data into .lib and .v| 
+Shell mode starts an interactive shell where users can enter a single command at a time. See **Command Syntax** for information on available commands.
 
-(2) **add command** for sequential cells
-**add_cell** for sequential cells
-(**add_cell** command shoul be one-line)
-| Command | Argument example | Description |
-|:-----------|------------:|:------------|
-| add_cell |  | add cell for characterize | 
-| -n cell_name | -n DFFSR | cell name in netlist|
-| -l logic_def. | -l DFF_PCPU_NRNS | logic of target cell (\*) |
-| -i inport | -i DATA | inport |
-| -c clock port | -c CLK | clock port |
-| -s set inport | -s SET | set port (optional) |
-| -r reset inport | -r RST | reset port (optional) |
-| -o outport | -o Q | outport |
-| -q storage | -q IQ IQN | storage elements |
-| -f func| Q=IQ QN=IQN | operation function | 
-
-Other **add command**(s) for sequential cells
+
+### Batchfile Mode
+Syntax: `python3 CharLib.py -b BATCHFILE`
+
+This mode takes in a text file BATCHFILE with one command on each line. Typically these files use the file extension ".cmd". You can include comments by starting the line with "#". 
+
+Batch files usually have three main sections: 
+
+1. Library configuration
+    - Here library-wide settings (such as output filenames) are configured. 
+2. Simulation configuration
+    - Here simulation data (such as VDD and VSS names and voltages) are configured.
+3. Cell-specific settings
+    - Here settings for each cell are provided. This varies depending on cell type, but usually includes cell name and SPICE files at minimum.
+
+See **Command Syntax** below for more information.
+
+
+### Automatic Mode (Coming soon)
+Syntax: `python3 CharLib.py -l LIBRARY_PATH`
+
+This mode parses a library of standard cells and attempts to generate a batchfile for that library. If successful, the batchfile is run immediately.
+
+There is some setup work required prior to running in automatic mode. Library and simulation configuration details must be added to a "lib_settings.json" file in directory specified by LIBRARY_PATH. An example of what this should look like is provided in the test/spice_osu350 folder. 
+
+If CharLib successfully generates a batchfile for the standard cell library, it will place that batchfile in the directory specified by LIBRARY_PATH. Subsequent runs in automatic mode will check for this batchfile and use it to run characterization. This allows users to make tweaks to the generated batchfile if desired. If you want to generate a new batchfile, you should delete the existing batchfile prior to running CharLib. 
+
+
+## Command Syntax
+
+### Library Configuration Commands
+These commands define configuration settings for all standard cells in the target library.
+
+| Command                | Argument example | Description |
+| ---------------------- | ---------------- | ----------- |
+| set_lib_name           | OSU035           | library name. (Default: unnamed_lib) |
+| set_dotlib_name        | OSU035.lib       | .lib file name. If unset, uses library name + .lib. |
+| set_verilog_name       | OSU035.v         | .v file name. If unset, uses library name + .v. |
+| set_cell_name_suffix   | OSU035_          | cell name suffix (optional) |
+| set_cell_name_prefix   | _V1              | cell name prefix (optional) |
+| set_voltage_unit       | V                | voltage unit. (Default: V) |
+| set_capacitance_unit   | pF               | capacitance unit. (Default: pF) |
+| set_resistance_unit    | Ohm              | resistance unit. (Default: Ω) |
+| set_current_unit       | mA               | current unit. (Default: μA) |
+| set_leakage_power_unit | pW               | power unit. (Default: nW) | 
+| set_time_unit          | ns               | time unit. (Default: ns) |
+| set_vdd_name           | VDD              | vdd name. Used to detect vdd. (Default: VDD) |
+| set_vss_name           | VSS              | vss name. Used to detect vss. (Default: VSS) |
+| set_pwell_name         | VPW              | pwell name. Used to detect pwell. (Default: VPW) |
+| set_nwell_name         | VNW              | nwell name. Used to detect nwell. (Default: VNW) |
+| set_run_sim            | True             | If false, reuse existing results to generate .lib and .v files. (Default: True) |
+
+
+### Simulation Configuration Commands
+These commands define simulation settings for all standard cells in the target library. 
+
+| Command                         | Argument example | Description |
+| ------------------------------- | ---------------- | ----------- |
+| set_process                     | typ              | define process condition (written into .lib) | 
+| set_temperature                 | 25               | simulation temperature  (written into .lib) |
+| set_vdd_voltage                 | 3.5              | simulation vdd voltage (unit in set_voltage_unit)|
+| set_vss_voltage                 | 0                | simulation vss voltage (unit in set_voltage_unit) |
+| set_pwell_voltage               | 0                | simulation pwell voltage (unit in set_voltage_unit) |
+| set_nwell_voltage               | 3.5              | simulation nwell voltage (unit in set_voltage_unit) |
+| set_logic_threshold_high        | 0.8              | logic threshold for slew table (ratio: 0~1) |
+| set_logic_threshold_low         | 0.2              | logic threshold for slew table (ratio: 0~1) |
+| set_logic_high_to_low_threshold | 0.5              | logic threshold for delay table (ratio: 0~1) |
+| set_logic_low_to_high_threshold | 0.5              | logic threshold for delay table (ratio: 0~1) |
+| set_work_dir                    | work             | simulation working directory |
+| set_simulator                   | /usr/bin/ngspice | binary for ngspice | 
+| set_energy_meas_low_threshold   | 0.01             | threshold to define voltage low for energy calculation (ratio:0~1) |
+| set_energy_meas_high_threshold  | 0.99             | threshold to define voltage high for energy calculation (ratio:0~1) |
+| set_energy_meas_time_extent     | 4                | simulation time extension for energy calculation target large output slew (real val.) |
+| set_operating_conditions        | PVT_3P5V_25C     | define operation condition (written into .lib) |
+
+Once library and simulation configuration is complete, use `initialize` to set up the characterizer work directory.
+| Command    | Argument example | Description           |
+| ---------- | ---------------- | --------------------- |
+| initialize | n/a              | Set up work directory | 
+
+### Cell-specific Configuration Commands
+These commands define settings specific to a single cell. 
+
+Cell-specific commands typically show up in blocks that initialize the cell and then add characterization parameters. After cells are added, they can be characterized using the `characterize` command.
+
+Cell command blocks should always start out with the `add_cell` or `add_flop` command. Use `add_cell` for combinational cells and `add_flop` with sequential cells. Unlike most other commands, `add_cell` and `add_flop` have several required arguments.
+
+#### `add_cell`
+This command should be on one line, e.g. `add_cell -n AND2X1 -i A B -o Y -f Y=A&B`
+| Command         | Argument example | Description |
+| --------------- | ---------------- | ----------- |
+| add_cell        |                  | Add a combinational cell to characterize |
+| -n cell_name    | -n HAX1          | Cell name in netlist |
+| -i inport       | -i A B           | List of input port names |
+| -o outport      | -o YC YS         | List of output port names |
+| -f verilog_func | -f YC=A&B YS=A^B | List of verilog functions describing how each output is determined from the inputs |
+
+> Note: The `-f` argument only accepts verilog functions using &, ^, |, ~, !, and parentheses. Other operators are not currently supported.
+
+Other **add** commands for combinational cells:
+| Command                 | Argument example     | Description |
+| ----------------------- | -------------------- | ----------- |
+| add_slope               | {1 4 16 64}          | input slew rates to test (unit in set_time_unit) |
+| add_load                | {1 4 16 64}          | output capacitive loads to test (unit in set_capacitance_unit) |
+| add_area                | 1                    | area (real val, no unit) |
+| add_netlist             | spice_dir/INVX1.spi  | location of netlist |
+| add_model               | spice_dir/model.sp   | location of model file (include simulation options) |
+| add_simulation_timestep | auto                 | simulation timestep. If set to `auto`, the simulator will use 1/10th of the minimum input slew rate as the timestep. | 
+
+#### `add_flop`
+Just like `add_cell`, the `add_flop` command should be on one line.
+| Command      | Argument example | Description |
+| ------------ | ---------------- |------------|
+| add_flop     |                  | Add a sequential cell to characterize |
+| -n cell_name | -n DFFSR         | Cell name in netlist|
+| -i inport    | -i DATA          | List of input port names |
+| -c clockport | -c CLK           | Clock port name |
+| -s setport   | -s SET           | Set port name (if present) |
+| -r resetport | -r RST           | Reset port name (if present) |
+| -o outport   | -o Q             | List of output port names |
+| -q storage   | -q IQ IQN        | List of storage element names |
+| -f func      | Q=IQ QN=IQN      | List of functions describing how each output relates to the inputs and storage elements |
+
+Other **add** commands for sequential cells:
 | Command | Argument example | Description |
-|:-----------|------------:|:------------|
+|-----------|------------|------------|
 | add_slope | {1 4 16 64} | slope index (unit in set_time_unit) | 
 | add_load  | {1 4 16 64}  | slope index (unit in set_capacitance_unit) | 
 | add_area  | 1  | area (real val, no unit) | 
@@ -176,15 +169,14 @@ Other **add command**(s) for sequential cells
 | add_simulation_hold_highest | 16 | manually set highst time for hold simulation (real val, unit in set_time_unit) |
 | add_simulation_hold_timestep | 5 | manually set timestep for hold simulation (real val, unit in set_time_unit) |
 
-**characterize** and **export** commands
-| Command | Argument example | Description |
-|:-----------|------------:|:------------|
-| characterize | n/a | run characterization |
-| export| n/a | export data into .lib and .v| 
+#### `charaacterize` and `export`
+| Command      | Argument example | Description |
+| ------------ | ---------------- | ----------- |
+| characterize | AND2X1 OR2X1     | Run characterization for the specified cells, or all cells if no arguments are passed. |
+| export       | AND2X1 OR2X1     | Export characterization results for the specified cells, or all cells if no arguments are passed. |
 
-### exit
-use **exit** command to return into shell.
+### `exit`
 | Command | Argument example | Description |
-|:-----------|------------:|:------------|
-| exit | n/a | exit |
+| ------- | ---------------- | ----------- |
+| exit    | n/a              | Exit to the parent shell.|
 
diff --git a/characterizer/Characterizer.py b/characterizer/Characterizer.py
index 540f7c7..9793fef 100644
--- a/characterizer/Characterizer.py
+++ b/characterizer/Characterizer.py
@@ -1,29 +1,38 @@
 import os, shutil
 
-from characterizer.LibrarySettings import LibrarySettings
-from characterizer.LogicCell import LogicCell, SequentialCell, RECOGNIZED_LOGIC
-from characterizer.char_comb import *
-from characterizer.char_seq import *
+from liberty.LibrarySettings import LibrarySettings
+from liberty.LogicCell import LogicCell, CombinationalCell, SequentialCell
 
 class Characterizer:
-    """Main object of Charlib. Keeps track of settings, cells, and results."""
-    
+    """Main object of Charlib. Keeps track of settings and cells."""
+
     def __init__(self) -> None:
         self.settings = LibrarySettings()
         self.cells = []
         self.num_files_generated = 0
 
+    def __str__(self) -> str:
+        lines = []
+        lines.append('Library settings:')
+        for line in str(self.settings).split('\n'):
+            lines.append(f'    {line}')
+        lines.append('Cells:')
+        for cell in self.cells:
+            for line in str(cell).split('\n'):
+                lines.append(f'    {line}')
+        return '\n'.join(lines)
+
     def target_cell(self) -> LogicCell:
         """Get last cell"""
-        return self.cells[len(self.cells) - 1]
+        return self.cells[-1]
 
-    def add_cell(self, name, logic, in_ports, out_ports, function):
+    def add_cell(self, name, in_ports, out_ports, functions, **kwargs):
         # Create a new logic cell
-        self.cells.append(LogicCell(name, logic, in_ports, out_ports, function))
+        self.cells.append(CombinationalCell(name, in_ports, out_ports, functions, **kwargs))
 
-    def add_flop(self, name, logic, in_ports, out_ports, clock_pin, set_pin, reset_pin, flops, functions):
+    def add_flop(self, name, in_ports, out_ports, clock_pin, flops, functions, **kwargs):
         # Create a new sequential cell
-        self.cells.append(SequentialCell(name, logic, in_ports, out_ports, clock_pin, set_pin, reset_pin, flops, functions))
+        self.cells.append(SequentialCell(name, in_ports, out_ports, clock_pin, flops, functions, **kwargs))
 
     def initialize_work_dir(self):
         if self.settings.run_sim:
@@ -32,272 +41,23 @@ def initialize_work_dir(self):
                 shutil.rmtree(self.settings.work_dir)
             self.settings.work_dir.mkdir()
         else:
-            print("Save previous working directory and files")
+            print("Reusing previous working directory and files")
 
-    def characterize(self, cell: LogicCell):
-        """Characterize a single cell"""
-        os.chdir(self.settings.work_dir)
+    def characterize(self, *cells):
+        """Characterize the passed cells, or all cells if none are passed"""
 
-        # Branch to each logic function
-        if(cell.logic == 'INV'):
-            print ("INV\n")
-            #                   [in0, out0]
-            expectationList2 = [['01','10'], ['10','01']]
-            return runCombIn1Out1(self.settings, cell, expectationList2,"neg")
-        elif(cell.logic == 'BUF'):
-            print ("BUF\n")
-            #                   [in0, out0]
-            expectationList2 = [['01','01'], ['10','10']]
-            return runCombIn1Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'AND2'):
-            print ("AND2\n")
-            #                   [in0, in1, out0]
-            expectationList2 = [['01','1','01'], ['10','1','10'],
-                                ['1','01','01'], ['1','10','10']]
-            return runCombIn2Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'AND3'):
-            print ("AND3\n")
-            #                   [in0, in1, in2, out0]
-            expectationList2 = [['01','1','1','01'], ['10','1','1','10'],
-                                ['1','01','1','01'], ['1','10','1','10'],
-                                ['1','1','01','01'], ['1','1','10','10']]
-            return runCombIn3Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'AND4'):
-            print ("AND4\n")
-            #                   [in0, in1, in2, in3,  out0]
-            expectationList2 = [['01','1','1','1','01'], ['10','1','1','1','10'],
-                                ['1','01','1','1','01'], ['1','10','1','1','10'],
-                                ['1','1','01','1','01'], ['1','1','10','1','10'],
-                                ['1','1','1','01','01'], ['1','1','1','10','10']]
-            return runCombIn4Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'OR2'):
-            print ("OR2\n")
-            #                   [in0, in1, out0]
-            expectationList2 = [['01','0','01'], ['10','0','10'],
-                                ['0','01','01'], ['0','10','10']]
-            return runCombIn2Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'OR3'):
-            print ("OR3\n")
-            #                   [in0, in1, in2, out0]
-            expectationList2 = [['01','0','0','01'], ['10','0','0','10'],
-                                ['0','01','0','01'], ['0','10','0','10'],
-                                ['0','0','01','01'], ['0','0','10','10']]
-            return runCombIn3Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'OR4'):
-            print ("OR4\n")
-            #                   [in0, in1, in2, in3, out0]
-            expectationList2 = [['01','0','0','0','01'], ['10','0','0','0','10'],
-                                ['0','01','0','0','01'], ['0','10','0','0','10'],
-                                ['0','0','01','0','01'], ['0','0','10','0','10'],
-                                ['0','0','0','01','01'], ['0','0','0','10','10']]
-            return runCombIn4Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'NAND2'):
-            print ("NAND2\n")
-            #                   [in0, in1, out0]
-            expectationList2 = [['01','1','10'], ['10','1','01'],
-                                ['1','01','10'], ['1','10','01']]
-            return runCombIn2Out1(self.settings, cell, expectationList2,"neg")
-        elif(cell.logic == 'NAND3'):
-            print ("NAND3\n")
-            #                   [in0, in1, in2, out0]
-            expectationList2 = [['01','1','1','10'], ['10','1','1','01'],
-                                ['1','01','1','10'], ['1','10','1','01'],
-                                ['1','1','01','10'], ['1','1','10','01']]
-            return runCombIn3Out1(self.settings, cell, expectationList2,"neg")
-        elif(cell.logic == 'NAND4'):
-            print ("NAND4\n")
-            #                   [in0, in1, in2, in3, out0]
-            expectationList2 = [['01','1','1','1','10'], ['10','1','1','1','01'],
-                                ['1','01','1','1','10'], ['1','10','1','1','01'],
-                                ['1','1','01','1','10'], ['1','1','10','1','01'],
-                                ['1','1','1','01','10'], ['1','1','1','10','01']]
-            return runCombIn4Out1(self.settings, cell, expectationList2,"neg")
-        elif(cell.logic == 'NOR2'):
-            print ("NOR2\n")
-            #                   [in0, in1, out0]
-            expectationList2 = [['01','0','10'], ['10','0','01'],
-                                ['0','01','10'], ['0','10','01']]
-            return runCombIn2Out1(self.settings, cell, expectationList2,"neg")
-        elif(cell.logic == 'NOR3'):
-            print ("NOR3\n")
-            #                   [in0, in1, in2, out0]
-            expectationList2 = [['01','0','0','10'], ['10','0','0','01'],
-                                ['0','01','0','10'], ['0','10','0','01'],
-                                ['0','0','01','10'], ['0','0','10','01']]
-            return runCombIn3Out1(self.settings, cell, expectationList2,"neg")
-        elif(cell.logic == 'NOR4'):
-            print ("NOR4\n")
-            #                   [in0, in1, in2, in3, out0]
-            expectationList2 = [['01','0','0','0','10'], ['10','0','0','0','01'],
-                                ['0','01','0','0','10'], ['0','10','0','0','01'],
-                                ['0','0','01','0','10'], ['0','0','10','0','01'],
-                                ['0','0','0','01','10'], ['0','0','0','10','01']]
-            return runCombIn4Out1(self.settings, cell, expectationList2,"neg")
-        elif(cell.logic == 'AO21'):
-            print ("AO21\n")
-            #                   [in0, in1, in2, out0]
-            expectationList2 = [['10','1','0','10'], ['01','1','0','01'],
-                                ['1','10','0','10'], ['1','01','0','01'],
-                                ['0','0','10','10'], ['0','0','01','01']]
-            return runCombIn3Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'AO22'):
-            print ("AO22\n")
-            #                   [in0, in1, in2, in3, out0]
-            expectationList2 = [['10','1','0','0','10'], ['01','1','0','0','01'],
-                                ['1','10','0','0','10'], ['1','01','0','0','01'],
-                                ['0','0','10','1','10'], ['0','0','01','1','01'],
-                                ['0','0','1','10','10'], ['0','0','1','01','01']]
-            return runCombIn4Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'OA21'):
-            print ("OA21\n")
-            #                   [in0, in1, in2, out0]
-            expectationList2 = [['10','0','1','10'], ['01','0','1','01'],
-                                ['0','10','1','10'], ['0','01','1','01'],
-                                ['0','1','10','10'], ['0','1','01','01']]
-            return runCombIn3Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'OA22'):
-            print ("OA22\n")
-            #                   [in0, in1, in2, in3, out0]
-            expectationList2 = [['10','1','0','1','10'], ['01','1','0','1','01'],
-                                ['0','10','0','1','10'], ['0','01','0','1','01'],
-                                ['0','1','10','0','10'], ['0','1','01','0','01'],
-                                ['0','1','0','10','10'], ['0','1','0','10','01']]
-            return runCombIn4Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'AOI21'):
-            print ("AOI21\n")
-            #                   [in0, in1, in2, out0]
-            expectationList2 = [['10','1','0','01'], ['01','1','0','10'],
-                                ['1','10','0','01'], ['1','01','0','10'],
-                                ['0','0','10','01'], ['0','0','01','10']]
-            return runCombIn3Out1(self.settings, cell, expectationList2,"neg")
-        elif(cell.logic == 'AOI22'):
-            print ("AOI22\n")
-            #                   [in0, in1, in2, in3, out0]
-            expectationList2 = [['10','1','0','0','01'], ['01','1','0','0','10'],
-                                ['1','10','0','0','01'], ['1','01','0','0','10'],
-                                ['0','0','10','1','01'], ['0','0','01','1','10'],
-                                ['0','0','1','10','01'], ['0','0','1','01','10']]
-            return runCombIn4Out1(self.settings, cell, expectationList2,"neg")
-        elif(cell.logic == 'OAI21'):
-            print ("OAI21\n")
-            #                   [in0, in1, in2, out0]
-            expectationList2 = [['10','0','1','01'], ['01','0','1','10'],
-                                ['0','10','1','01'], ['0','01','1','10'],
-                                ['0','1','10','01'], ['0','1','01','10']]
-            return runCombIn3Out1(self.settings, cell, expectationList2,"neg")
-        elif(cell.logic == 'OAI22'):
-            print ("OAI22\n")
-            #                   [in0, in1, in2, in3, out0]
-            expectationList2 = [['10','1','0','1','01'], ['01','1','0','1','10'],
-                                ['0','10','0','1','01'], ['0','01','0','1','10'],
-                                ['0','1','10','0','01'], ['0','1','01','0','10'],
-                                ['0','1','0','10','01'], ['0','1','0','10','10']]
-            return runCombIn4Out1(self.settings, cell, expectationList2,"neg")
-        elif(cell.logic == 'XOR2'):
-            print ("XOR2\n")
-            #                   [in0, in1, out0]
-            expectationList2 = [['01','0','01'], ['10','0','10'],
-                                ['01','1','10'], ['10','1','01'],
-                                ['0','01','01'], ['0','10','10'],
-                                ['1','01','10'], ['1','10','01']]
-            return runCombIn2Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'XNOR2'):
-            print ("XNOR2\n")
-            #                   [in0, in1, out0]
-            expectationList2 = [['01','0','10'], ['10','0','01'],
-                                ['01','1','01'], ['10','1','10'],
-                                ['0','01','10'], ['0','10','01'],
-                                ['1','01','01'], ['1','10','10']]
-            return runCombIn2Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'SEL2'):
-            print ("SEL2\n")
-            #                   [in0, in1, sel, out]
-            expectationList2 = [['01','0','0','01'], ['10','0','0','10'],
-                                ['0','01','1','01'], ['0','10','1','10'],
-                                ['1','0','01','10'], ['1','0','10','01'],
-                                ['0','1','01','01'], ['0','1','10','10']]
-            return runCombIn3Out1(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'HA'):
-            print ("HA\n")
-            #                   [in0, in1, cout, sum]
-            expectationList2 = [['01','0','0','01'],  ['10','0','0','10'],
-                                ['0','01','0','01'],  ['0','10','0','10'],
-                                ['01','1','01','10'], ['10','1','10','10'],
-                                ['1','01','01','10'], ['1','10','10','01']]
-            return runCombIn2Out2(self.settings, cell, expectationList2,"pos")
-        elif(cell.logic == 'FA'):
-            print ("FA\n")
-            ##                  [in0, in1, sel, cout, sum]
-            expectationList2 = [['01','0','0','0','01'],  ['10','0','0','0','10'],
-                                ['0','01','0','0','01'],  ['0','10','0','0','10'],
-                                ['0','0','01','0','01'],  ['0','0','10','0','10'],
-                                ['01','1','0','01','10'], ['10','1','0','10','01'],
-                                ['01','0','1','01','10'], ['10','0','1','10','01'],
-                                ['1','01','0','01','10'], ['1','10','0','10','01'],
-                                ['0','01','1','01','10'], ['0','10','1','10','01'],
-                                ['1','0','01','01','10'], ['1','0','10','10','01'],
-                                ['0','1','01','01','10'], ['0','1','10','10','01'],
-                                ['01','1','1','1','01'],  ['10','1','1','1','10'],
-                                ['1','01','1','1','01'],  ['1','10','1','1','10'],
-                                ['1','1','01','1','01'],  ['1','1','10','1','10']]
-            return runCombIn3Out2(self.settings, cell, expectationList2,"pos")
+        # If no target cells were given, characterize all cells
+        for cell in cells if cells else self.cells:
+            cell.characterize(self.settings)
 
-        ## Branch to sequential logics
-        elif(cell.logic == 'DFF_PCPU'):
-            print ("DFF, positive clock, positive unate\n")
-            ## D1 & C01 -> Q01
-            ## D0 & C01 -> Q10
-            #                   [D,   C,     Q]
-            expectationList2 = [['01','0101','01'],
-                                ['10','0101','10']]
-            return runFlop(self.settings, cell, expectationList2)
-        elif(cell.logic == 'DFF_PCNU'):
-            print ("DFF, positive clock, negative unate\n")
-            ## D1 & C01 -> Q01
-            ## D0 & C01 -> Q10
-            #                   [D,   C,     Q]
-            expectationList2 = [['01','0101','10'],
-                                ['10','0101','01']]
-            return runFlop(self.settings, cell, expectationList2)
-        elif(cell.logic == 'DFF_NCPU'):
-            print ("DFF, negative clock, positive unate\n")
-            ## D1 & C01 -> Q01
-            ## D0 & C01 -> Q10
-            #                   [D,   C,     Q]
-            expectationList2 = [['01','1010','01'],
-                                ['10','1010','10']]
-            return runFlop(self.settings, cell, expectationList2)
-        elif(cell.logic == 'DFF_NCNU'):
-            print ("DFF, negative clock, negative unate\n")
-            ## D1 & C01 -> Q01
-            ## D0 & C01 -> Q10
-            #                   [D,   C,     Q]
-            expectationList2 = [['01','1010','10'],
-                                ['10','1010','01']] 
-            return runFlop(self.settings, cell, expectationList2)
-        elif(cell.logic == 'DFF_PCPU_NR'):
-            print ("DFF, positive clock, positive unate, async neg-reset\n")
-            ## D1 & C01 -> Q01
-            ## D0 & C01 -> Q10
-            ## R01      -> Q10
-            #                   [D,   C,    R,    Q]
-            expectationList2 = [['01','0101', '1','01'],
-                                ['10','0101', '1','10'],
-                                [ '1','0101','01','10']]
-            return runFlop(self.settings, cell, expectationList2)
-        elif(cell.logic == 'DFF_PCPU_NRNS'):
-            print ("DFF, positive clock, positive unate, async neg-reset, async neg-set\n")
-            ## D1 & C01 -> Q01 QN10
-            ## D0 & C01 -> Q10 QN01
-            ## S01      -> Q01 QN10
-            ## R01      -> Q10 QN01
-            ##                  [D,   C,  S,   R,    Q]
-            expectationList2 = [['01','0101','1', '1', '01'],
-                                ['10','0101','1', '1', '10'],
-                                ['0','0101','01', '1', '01'],
-                                ['1','0101', '1','01', '10']]
-            return runFlop(self.settings, cell, expectationList2)
-        else:
-            print ("Target logic:"+cell.logic+" is not registered for characterization!\n")
-            print ("Add characterization function for this program! -> die\n")
-            exit()
+    def print_msg(self, message: str):
+        if not self.settings.suppress_message:
+            print(message)
+    
+    def print_sim(self, message: str):
+        if not self.settings.suppress_sim_message:
+            print(f'SIM: {message}')
+    
+    def print_debug(self, message: str):
+        if not self.settings.suppress_debug_message:
+            print(f'DEBUG: {message}')
\ No newline at end of file
diff --git a/characterizer/ExportUtils.py b/characterizer/ExportUtils.py
deleted file mode 100644
index e3341d1..0000000
--- a/characterizer/ExportUtils.py
+++ /dev/null
@@ -1,852 +0,0 @@
-
-import re, sys
-
-from characterizer.LogicCell import LogicCell, SequentialCell
-
-def exportFiles(targetLib, targetCell):
-    if not targetLib.is_exported:
-        exportLib(targetLib, targetCell)
-    if targetLib.is_exported and not targetCell.is_exported:
-        ## export comb. logic
-        if not isinstance(targetCell, SequentialCell):
-            exportHarness(targetLib, targetCell)
-            exportVerilog(targetLib, targetCell)
-        ## export seq. logic
-        else:
-            exportHarnessFlop(targetLib, targetCell)
-            exportVerilogFlop(targetLib, targetCell)
-
-## export library definition to .lib
-def exportLib(targetLib, targetCell):
-    """Export library definition to liberty file"""
-    outlines = []
-    ## general settings
-    outlines.append(f'library ({targetLib.lib_name}) {{\n')
-    outlines.append(f'  delay_model : "{targetLib.delay_model}";\n')
-    outlines.append(f'  capacitive_load_unit (1,{str(targetLib.units.capacitance)});\n')
-    outlines.append(f'  current_unit : "1{str(targetLib.units.current)}";\n')
-    outlines.append(f'  leakage_power_unit : "1{str(targetLib.units.leakage_power)}";\n')
-    outlines.append(f'  pulling_resistance_unit : "1{str(targetLib.units.resistance)}";\n')
-    outlines.append(f'  time_unit : "1{str(targetLib.units.time)}";\n')
-    outlines.append(f'  voltage_unit : "1{str(targetLib.units.voltage)}";\n')
-    outlines.append(f'  voltage_map ({str(targetLib.vdd.name)}, {str(targetLib.vdd.voltage)});\n')
-    outlines.append(f'  voltage_map ({str(targetLib.vss.name)}, {str(targetLib.vss.voltage)});\n')
-    outlines.append(f'  voltage_map (GND , {str(targetLib.vss.voltage)});\n')
-    outlines.append(f'  default_cell_leakage_power : 0;\n')
-    outlines.append(f'  default_fanout_load : 1;\n')
-    outlines.append(f'  default_max_transition : 1000;\n')
-    outlines.append(f'  default_input_pin_cap : 0;\n')
-    outlines.append(f'  default_inout_pin_cap : 0;\n')
-    outlines.append(f'  default_leakage_power_density : 0;\n')
-    outlines.append(f'  default_max_fanout : 100;\n')
-    outlines.append(f'  default_output_pin_cap : 0;\n')
-    outlines.append(f'  in_place_swap_mode : match_footprint;\n')
-    outlines.append(f'  input_threshold_pct_fall : {str(targetLib.logic_high_to_low_threshold*100)};\n')
-    outlines.append(f'  input_threshold_pct_rise : {str(targetLib.logic_low_to_high_threshold*100)};\n')
-    outlines.append(f'  nom_process : 1;\n')
-    outlines.append(f'  nom_temperature : "{str(targetLib.temperature)}";\n')
-    outlines.append(f'  nom_voltage : "{str(targetLib.vdd.voltage)}";\n')
-    outlines.append(f'  output_threshold_pct_fall : {str(targetLib.logic_high_to_low_threshold*100)};\n')
-    outlines.append(f'  output_threshold_pct_rise : {str(targetLib.logic_low_to_high_threshold*100)};\n')
-    outlines.append(f'  slew_derate_from_library : 1;\n')
-    outlines.append(f'  slew_lower_threshold_pct_fall : {str(targetLib.logic_threshold_low*100)};\n')
-    outlines.append(f'  slew_lower_threshold_pct_rise : {str(targetLib.logic_threshold_low*100)};\n')
-    outlines.append(f'  slew_upper_threshold_pct_fall : {str(targetLib.logic_threshold_high*100)};\n')
-    outlines.append(f'  slew_upper_threshold_pct_rise : {str(targetLib.logic_threshold_high*100)};\n')
-    ## operating conditions
-    outlines.append(f'  operating_conditions ({targetLib.operating_conditions}) {{\n')
-    outlines.append(f'    process : 1;\n')
-    outlines.append(f'    temperature : {str(targetLib.temperature)};\n')
-    outlines.append(f'    voltage : {str(targetLib.vdd.voltage)};\n')
-    outlines.append(f'  }}\n')
-    outlines.append(f'  default_operating_conditions : {targetLib.operating_conditions};\n')
-    outlines.append(f'  lu_table_template (constraint_template) {{\n')
-    outlines.append(f'    variable_1 : constrained_pin_transition;\n')
-    outlines.append(f'    variable_2 : related_pin_transition;\n')
-    outlines.append(f'    index_1 {targetCell.return_slope()}\n')
-    outlines.append(f'    index_2 {targetCell.return_slope()}\n')
-    outlines.append(f'  }}\n')
-    outlines.append(f'  lu_table_template (delay_template) {{\n')
-    outlines.append(f'    variable_1 : input_net_transition;\n')
-    outlines.append(f'    variable_2 : total_output_net_capacitance;\n')
-    outlines.append(f'    index_1 {targetCell.return_slope()}\n')
-    outlines.append(f'    index_2 {targetCell.return_load()}\n')
-    outlines.append(f'  }}\n')
-    outlines.append(f'  lu_table_template (recovery_template) {{\n')
-    outlines.append(f'    variable_1 : related_pin_transition;\n')
-    outlines.append(f'    variable_2 : constrained_pin_transition;\n')
-    outlines.append(f'    index_1 {targetCell.return_slope()}\n')
-    outlines.append(f'    index_2 {targetCell.return_slope()}\n')
-    outlines.append(f'  }}\n')
-    outlines.append(f'  lu_table_template (removal_template) {{\n')
-    outlines.append(f'    variable_1 : related_pin_transition;\n')
-    outlines.append(f'    variable_2 : constrained_pin_transition;\n')
-    outlines.append(f'    index_1 {targetCell.return_slope()}\n')
-    outlines.append(f'    index_2 {targetCell.return_slope()}\n')
-    outlines.append(f'  }}\n')
-    outlines.append(f'  lu_table_template (mpw_constraint_template) {{\n')
-    outlines.append(f'    variable_1 : constrained_pin_transition;\n')
-    outlines.append(f'    index_1 {targetCell.return_slope()}\n')
-    outlines.append(f'  }}\n')
-    outlines.append(f'  power_lut_template (passive_power_template) {{\n')
-    outlines.append(f'    variable_1 : input_transition_time;\n')
-    outlines.append(f'    index_1 {targetCell.return_slope()}\n')
-    outlines.append(f'  }}\n')
-    outlines.append(f'  power_lut_template (power_template) {{\n')
-    outlines.append(f'    variable_1 : input_transition_time;\n')
-    outlines.append(f'    variable_2 : total_output_net_capacitance;\n')
-    outlines.append(f'    index_1 {targetCell.return_slope()}\n')
-    outlines.append(f'    index_2 {targetCell.return_load()}\n')
-    outlines.append(f'  }}\n')
-    outlines.append(f'  input_voltage (default_{targetLib.vdd.name}_{targetLib.vss.name}_input) {{\n')
-    outlines.append(f'    vil : {str(targetLib.vss.voltage)};\n')
-    outlines.append(f'    vih : {str(targetLib.vdd.voltage)};\n')
-    outlines.append(f'    vimin : {str(targetLib.vss.voltage)};\n')
-    outlines.append(f'    vimax : {str(targetLib.vdd.voltage)};\n')
-    outlines.append(f'  }}\n')
-    outlines.append(f'  output_voltage (default_{targetLib.vdd.name}_{targetLib.vss.name}_output) {{\n')
-    outlines.append(f'    vol : {str(targetLib.vss.voltage)};\n')
-    outlines.append(f'    voh : {str(targetLib.vdd.voltage)};\n')
-    outlines.append(f'    vomin : {str(targetLib.vss.voltage)};\n')
-    outlines.append(f'    vomax : {str(targetLib.vdd.voltage)};\n')
-    outlines.append(f'  }}\n')
-    
-    with open(targetLib.dotlib_name, 'w') as f:
-        f.writelines(outlines)
-        f.close()
-    targetLib.set_exported()
-
-    ## for verilog file 
-    outlines = []
-    outlines.append(f'// Verilog model for {targetLib.lib_name};\n')
-    # TODO: Implement this
-    with open(targetLib.verilog_name, 'w') as f:
-        f.writelines(outlines)
-        f.close()
-
-## export harness data to .lib
-def exportHarness(targetLib, targetCell):
-    harnesses = targetCell.harnesses
-    print(harnesses[0])
-    outlines = []
-    outlines.append(f'  cell ({targetCell.name}) {{\n') ## cell start
-    outlines.append(f'    area : {str(targetCell.area)};\n')
-    ##outlines.append("    cell_leakage_power : "+targetCell.leakage_power+";\n")
-    outlines.append(f'    cell_leakage_power : {harnesses[0][0].pleak};\n') ## use leak of 1st harness
-    outlines.append(f'    pg_pin ({targetLib.vdd.name}) {{\n')
-    outlines.append(f'      pg_type : primary_power;\n')
-    outlines.append(f'      voltage_name : "{targetLib.vdd.name}";\n')
-    outlines.append(f'    }}\n')
-    outlines.append(f'    pg_pin ({targetLib.vss.name}) {{\n')
-    outlines.append(f'      pg_type : primary_ground;\n')
-    outlines.append(f'      voltage_name : "{targetLib.vss.name}";\n')
-    outlines.append(f'    }}\n')
-
-    ## select one output pin from pinlist(target_outports) 
-    for target_outport in targetCell.out_ports:
-        index1 = targetCell.out_ports.index(target_outport) 
-        outlines.append(f'    pin ({target_outport}) {{\n') ## out pin start
-        outlines.append(f'      direction : output;\n')
-        outlines.append(f'      function : "{targetCell.functions[index1]})"\n')
-        outlines.append(f'      related_power_pin : "{targetLib.vdd.name}";\n')
-        outlines.append(f'      related_ground_pin : "{targetLib.vss.name}";\n')
-        outlines.append(f'      max_capacitance : "{str(targetCell.load[-1])}";\n') ## use max val. of load table
-        outlines.append(f'      output_voltage : default_{targetLib.vdd.name}_{targetLib.vss.name}_output;\n')
-        ## timing
-        for target_inport in targetCell.in_ports:
-            outlines.append(f'      timing () {{\n')
-            index2 = targetCell.in_ports.index(target_inport) 
-            outlines.append(f'        related_pin : "{target_inport}";\n')
-            outlines.append(f'        timing_sense : "{harnesses[index1][index2*2].timing_sense}";\n')
-            outlines.append(f'        timing_type : "{harnesses[index1][index2*2].timing_type}";\n')
-            ## rise
-            ## propagation delay
-            outlines.append(f'        {harnesses[index1][index2*2].direction_prop} (delay_template) {{\n')
-            for lut_line in harnesses[index1][index2*2].lut_prop:
-                outlines.append(f'          {lut_line}\n')
-            outlines.append(f'        }}\n') 
-            ## transition delay
-            outlines.append(f'        {harnesses[index1][index2*2].direction_tran} (delay_template) {{\n')
-            for lut_line in harnesses[index1][index2*2].lut_tran:
-                outlines.append(f'          {lut_line}\n')
-            outlines.append(f'        }}\n') 
-            ## fall
-            ## propagation delay
-            outlines.append(f'        {harnesses[index1][index2*2+1].direction_prop} (delay_template) {{\n')
-            for lut_line in harnesses[index1][index2*2+1].lut_prop:
-                outlines.append(f'          {lut_line}\n')
-            outlines.append(f'        }}\n') 
-            ## transition delay
-            outlines.append(f'        {harnesses[index1][index2*2+1].direction_tran} (delay_template) {{\n')
-            for lut_line in harnesses[index1][index2*2+1].lut_tran:
-                outlines.append(f'          {lut_line}\n')
-            outlines.append(f'        }}\n') 
-            outlines.append(f'      }}\n') ## timing end 
-        ## power
-        for target_inport in targetCell.in_ports:
-            outlines.append(f'      internal_power () {{\n')
-            index2 = targetCell.in_ports.index(target_inport) 
-            outlines.append(f'        related_pin : "{target_inport}";\n')
-            ## rise(fall)
-            outlines.append(f'        {harnesses[index1][index2*2].direction_power} (power_template) {{\n')
-            for lut_line in harnesses[index1][index2*2].lut_eintl:
-                outlines.append(f'          {lut_line}\n')
-            outlines.append(f'        }}\n') 
-            ## fall(rise)
-            outlines.append(f'        {harnesses[index1][index2*2+1].direction_power} (power_template) {{\n')
-            for lut_line in harnesses[index1][index2*2+1].lut_eintl:
-                outlines.append(f'          {lut_line}\n')
-            outlines.append(f'        }}\n') 
-            outlines.append(f'      }}\n') ## power end 
-        outlines.append(f'    }}\n') ## out pin end
-
-    ## select one input pin from pinlist(target_inports) 
-    for target_inport in targetCell.in_ports:
-        index1 = targetCell.in_ports.index(target_inport) 
-        outlines.append(f'    pin ({target_inport}){{\n') ## out pin start
-        outlines.append(f'      direction : input; \n')
-        outlines.append(f'      related_power_pin : {targetLib.vdd.name};\n')
-        outlines.append(f'      related_ground_pin : {targetLib.vss.name};\n')
-        outlines.append(f'      max_transition : {str(targetCell.in_slopes[-1])};\n')
-        outlines.append(f'      capacitance : "{str(targetCell.cins[index1])}";\n')
-        outlines.append(f'      input_voltage : default_{targetLib.vdd.name}_{targetLib.vss.name}_input;\n')
-        outlines.append(f'    }}\n') ## in pin end
-
-    outlines.append(f'  }}\n') ## cell end
-    with open(targetLib.dotlib_name, 'a') as f:
-        f.writelines(outlines)
-        f.close()
-    targetCell.set_exported()
-
-
-## export harness data to .lib
-def exportHarnessFlop(targetLib, targetCell):
-    harnesses = targetCell.harnesses
-    outlines = []
-    outlines.append("  cell ("+targetCell.name+") {\n") #### cell start
-    outlines.append("    area : "+str(targetCell.area)+";\n")
-    # outlines.append("    cell_leakage_power : "+targetCell.leakage_power+";\n")
-    outlines.append("    pg_pin ("+targetLib.vdd.name+"){\n")
-    outlines.append("      pg_type : primary_power;\n")
-    outlines.append("      voltage_name : \""+targetLib.vdd.name+"\";\n")
-    outlines.append("    }\n")
-    outlines.append("    pg_pin ("+targetLib.vss.name+"){\n")
-    outlines.append("      pg_type : primary_ground;\n")
-    outlines.append("      voltage_name : \""+targetLib.vss.name+"\";\n")
-    outlines.append("    }\n")
-
-    ## define flop
-    outlines.append("    ff ("+str(targetCell.flops[0])+","+str(targetCell.flops[1])+"){\n") 
-    outlines.append("    clocked_on : \""+targetCell.clock+"\";\n") 
-    for target_inport in targetCell.in_ports:
-        outlines.append("    next_state : \""+target_inport+"\";\n") 
-    if targetCell.reset is not None:
-        outlines.append("    clear : \""+targetCell.reset+"\";\n") 
-    if targetCell.set is not None:
-        outlines.append("    preset : \""+targetCell.set+"\";\n") 
-        if targetCell.reset is not None:
-            ## value when set and reset both activate
-            ## tool does not support this simulation, so hard coded to low
-            outlines.append("    clear_preset_var1 : L ;\n") 
-            outlines.append("    clear_preset_var2 : L ;\n") 
-    outlines.append("    }\n") 
-    ##
-    ## (1) clock
-    ##
-    if targetCell.clock is not None:
-        # index1 = targetCell.clock.index(target_clock) 
-        index1 = 0 
-        target_inport = targetCell.clock
-        outlines.append("    pin ("+target_inport+"){\n") ## clock pin start 
-        outlines.append("      direction : input;\n")
-        outlines.append("      related_power_pin : \""+targetLib.vdd.name+"\";\n")
-        outlines.append("      related_ground_pin : \""+targetLib.vss.name+"\";\n")
-        outlines.append("      max_transition : "+str(targetCell.in_slopes[-1])+";\n")
-        outlines.append("      capacitance : \""+str(targetCell.cclks[index1])+"\";\n")
-        outlines.append("      input_voltage : default_"+targetLib.vdd.name+"_"+targetLib.vss.name+"_input;\n")
-        outlines.append("      clock : true;\n") 
-        # internal_power() {
-        #     rise_power(passive_energy_template_7x1) {
-        #     index_1 ("0.01669, 0.030212, 0.094214, 0.230514, 0.461228, 0.919101, 2.29957");
-        #     values ("0.040923, 0.0374, 0.044187, 0.046943, 0.045139, 0.051569, 0.080466");
-        #     }
-        #     fall_power(passive_energy_template_7x1) {
-        #     index_1 ("0.016236, 0.030811, 0.094437, 0.232485, 0.461801, 0.919137, 2.29958");
-        #     values ("0.038868, 0.03793, 0.033501, 0.043008, 0.042432, 0.048973, 0.08357");
-        #     }
-        # }
-        # min_pulse_width_high : 0.145244;
-        # min_pulse_width_low : 0.226781;
-        outlines.append("    }\n") ## clock pin end
-
-
-
-    ##
-    ## (2) prop/tran/setup/hold for input pins 
-    ##
-    for target_inport in targetCell.in_ports:
-        for target_outport in targetCell.out_ports:
-            ## select inport with setup/hold informatioin
-            index2 = targetCell.in_ports.index(target_inport) 
-            index1 = targetCell.out_ports.index(target_outport) 
-            #print(harnessList2[index1][index2*2].timing_type_setup)
-            if((harnesses[index1][index2*2].timing_type_setup == "setup_rising") or (harnesses[index1][index2*2].timing_type_setup == "setup_falling")):
-                outlines.append("    pin ("+target_inport+"){\n") #### inport pin start 
-                outlines.append("      direction : input;\n")
-                outlines.append("      related_power_pin : \""+targetLib.vdd.name+"\";\n")
-                outlines.append("      related_ground_pin : \""+targetLib.vss.name+"\";\n")
-                outlines.append("      max_transition : "+str(targetCell.in_slopes[-1])+";\n")
-                outlines.append("      capacitance : \""+str(targetCell.cins[index1])+"\";\n")
-                outlines.append("      input_voltage : default_"+targetLib.vdd.name+"_"+targetLib.vss.name+"_input;\n")
-                ## (2-1) setup
-                outlines.append("      timing () {\n")
-                outlines.append("        related_pin : \""+targetCell.clock+"\";\n")
-                outlines.append("        timing_type : \""+harnesses[index1][index2*2].timing_type_setup+"\";\n")
-                ## setup rise
-                outlines.append("        "+harnesses[index1][index2*2].timing_sense_setup+" (constraint_template) {\n")
-                for lut_line in harnesses[index1][index2*2].lut_setup:
-                    outlines.append("          "+lut_line+"\n")
-                outlines.append("        }\n") 
-                ## setup fall
-                outlines.append("        "+harnesses[index1][index2*2+1].timing_sense_setup+" (constraint_template) {\n")
-                for lut_line in harnesses[index1][index2*2+1].lut_setup:
-                    outlines.append("          "+lut_line+"\n")
-                outlines.append("        }\n") 
-                outlines.append("      }\n") 
-                ## (2-2) hold
-                outlines.append("      timing () {\n")
-                index1 = targetCell.out_ports.index(target_outport) 
-                outlines.append("        related_pin : \""+targetCell.clock+"\";\n")
-                outlines.append("        timing_type : \""+harnesses[index1][index2*2].timing_type_hold+"\";\n")
-                ## hold rise
-                outlines.append("        "+harnesses[index1][index2*2].timing_sense_hold+" (constraint_template) {\n")
-                for lut_line in harnesses[index1][index2*2].lut_hold:
-                    outlines.append("          "+lut_line+"\n")
-                outlines.append("        }\n") 
-                ## hold fall
-                outlines.append("        "+harnesses[index1][index2*2+1].timing_sense_hold+" (constraint_template) {\n")
-                for lut_line in harnesses[index1][index2*2+1].lut_hold:
-                    outlines.append("          "+lut_line+"\n")
-                outlines.append("        }\n") 
-                outlines.append("      }\n") 
-    outlines.append("    }\n") ## inport pin end
-
-    ##
-    ## (3) outport 
-    ##
-    for target_outport in targetCell.out_ports:
-        index1 = targetCell.out_ports.index(target_outport) 
-        outlines.append("    pin ("+target_outport+"){\n") #### out pin start
-        outlines.append("      direction : output;\n")
-        outlines.append("      function : \"("+targetCell.functions[index1]+")\"\n")
-        outlines.append("      related_power_pin : \""+targetLib.vdd.name+"\";\n")
-        outlines.append("      related_ground_pin : \""+targetLib.vss.name+"\";\n")
-        outlines.append("      max_capacitance : \""+str(targetCell.load[-1])+"\";\n") ## use max val. of load table
-        outlines.append("      output_voltage : default_"+targetLib.vdd.name+"_"+targetLib.vss.name+"_output;\n")
-        ## (3-1) clock
-        if targetCell.clock is not None:
-            ## index2 is a base pointer for harness search
-            ## index2_offset and index2_offset_max are used to 
-            ## search harness from harnessList2 which contain "timing_type_set"
-            index2 = targetCell.out_ports.index(target_outport) 
-            index2_offset = 0
-            index2_offset_max = 10
-            while(index2_offset < index2_offset_max):
-                if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_setup"):
-                    break
-                index2_offset += 1
-            if(index2_offset == 10):
-                print("Error: index2_offset exceed max. search number\n")
-                exit()
-
-            ##target_inport = targetCell.clock
-            outlines.append("      timing () {\n")
-            outlines.append("        related_pin : \""+targetCell.clock+"\";\n")
-            outlines.append("        timing_type : \""+harnesses[index1][index2*2+index2_offset].timing_type_clock+"\";\n")
-            outlines.append("        timing_sense : \""+harnesses[index1][index2*2+index2_offset].timing_sense_clock+"\";\n")
-            #### (3-1-1) rise
-            ## propagation delay
-            outlines.append("        "+harnesses[index1][index2*2+index2_offset].direction_prop+" (delay_template) {\n")
-            for lut_line in harnesses[index1][index2*2+index2_offset].lut_prop:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            ## transition delay
-            outlines.append("        "+harnesses[index1][index2*2+index2_offset].direction_tran+" (delay_template) {\n")
-            for lut_line in harnesses[index1][index2*2+index2_offset].lut_tran:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            #### (3-1-2) fall
-            ## propagation delay
-            outlines.append("        "+harnesses[index1][index2*2+index2_offset+1].direction_prop+" (delay_template) {\n")
-            for lut_line in harnesses[index1][index2*2+index2_offset+1].lut_prop:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            ## transition delay
-            outlines.append("        "+harnesses[index1][index2*2+index2_offset+1].direction_tran+" (delay_template) {\n")
-            for lut_line in harnesses[index1][index2*2+index2_offset+1].lut_tran:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            outlines.append("      }\n") ## timing end 
-        ##outlines.append("    }\n") ## out pin end -> continue for reset
-
-        ## (3-2) reset (one directrion)
-        if targetCell.reset is not None:
-            ##target_inport = targetCell.reset
-            outlines.append("      timing () {\n")
-
-            ## Harness search for reset
-            ## index2 is an base pointer for harness search
-            ## index2_offset and index2_offset_max are used to 
-            ## search harness from harnessList2 which contain "timing_type_set"
-            index2 = targetCell.out_ports.index(target_outport) 
-            index2_offset = 0
-            index2_offset_max = 10
-            while(index2_offset < index2_offset_max):
-                #print(harnessList2[index1][index2*2+index2_offset])
-                if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_reset"):
-                    break
-                index2_offset += 1
-            if(index2_offset == 10):
-                print("Error: index2_offset exceed max. search number\n")
-                exit()
-
-            outlines.append("        related_pin : \""+targetCell.reset+"\";\n")
-            outlines.append("        timing_sense : \""+harnesses[index1][index2*2+index2_offset].timing_sense_reset+"\";\n")
-            outlines.append("        timing_type : \""+harnesses[index1][index2*2+index2_offset].timing_type_reset+"\";\n")
-            ## (3-2-1) propagation delay
-            outlines.append("        "+harnesses[index1][index2*2+index2_offset].direction_reset_prop+" (delay_template) {\n")
-            for lut_line in harnesses[index1][index2*2+index2_offset].lut_prop:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            ## (3-2-2) transition delay
-            outlines.append("        "+harnesses[index1][index2*2+index2_offset].direction_reset_tran+" (delay_template) {\n")
-            for lut_line in harnesses[index1][index2*2+index2_offset].lut_tran:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            outlines.append("      }\n") 
-        ##outlines.append("    }\n") #### out pin end -> continue for set
-
-        ## (3-3) set (one directrion)
-        if targetCell.set is not None:
-            ##target_inport = targetCell.set
-            outlines.append("      timing () {\n")
-            ## Harness search for set
-            ## index2 is an base pointer for harness search
-            ## index2_offset and index2_offset_max are used to 
-            ## search harness from harnessList2 which contain "timing_type_set"
-            index2 = targetCell.out_ports.index(target_outport) 
-            index2_offset = 0
-            index2_offset_max = 10
-            while(index2_offset < index2_offset_max):
-                if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_set"):
-                    break
-                index2_offset += 1
-            if(index2_offset == 10):
-                print("Error: index2_offset exceed max. search number\n")
-                exit()
-
-            outlines.append("        related_pin : \""+targetCell.set+"\";\n")
-            outlines.append("        timing_sense : \""+harnesses[index1][index2*2+index2_offset].timing_sense_set+"\";\n")
-            outlines.append("        timing_type : \""+harnesses[index1][index2*2+index2_offset].timing_type_set+"\";\n")
-            ## (3-3-1) propagation delay
-            outlines.append("        "+harnesses[index1][index2*2+index2_offset].direction_set_prop+" (delay_template) {\n")
-            for lut_line in harnesses[index1][index2*2+index2_offset].lut_prop:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            ## (3-3-2) transition delay
-            outlines.append("        "+harnesses[index1][index2*2+index2_offset].direction_set_tran+" (delay_template) {\n")
-            for lut_line in harnesses[index1][index2*2+index2_offset].lut_tran:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            outlines.append("      }\n") 
-        ##outlines.append("    }\n") #### out pin end
-        
-        ## (3-4) clock power
-        if targetCell.clock is not None:
-            ## index2 is a base pointer for harness search
-            ## index2_offset and index2_offset_max are used to 
-            ## search harness from harnessList2 which contain "timing_type_set"
-            index2 = targetCell.out_ports.index(target_outport) 
-            index2_offset = 0
-            index2_offset_max = 10
-            while(index2_offset < index2_offset_max):
-                if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_setup"):
-                    break
-                index2_offset += 1
-            if(index2_offset == 10):
-                print("Error: index2_offset exceed max. search number\n")
-                exit()
-
-            outlines.append("      internal_power () {\n")
-            index2 = targetCell.in_ports.index(target_inport) 
-            outlines.append("        related_pin : \""+targetCell.clock+"\";\n")
-            ## rise(fall)
-            outlines.append("        "+harnesses[index1][index2*2].direction_power+" (power_template) {\n")
-            for lut_line in harnesses[index1][index2*2].lut_eintl:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            ## fall(rise)
-            outlines.append("        "+harnesses[index1][index2*2+1].direction_power+" (power_template) {\n")
-            for lut_line in harnesses[index1][index2*2+1].lut_eintl:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            outlines.append("      }\n") ## power end 
-
-        ## (3-5) reset power 
-        if targetCell.reset is not None:
-
-            ## Harness search for reset
-            ## index2 is an base pointer for harness search
-            ## index2_offset and index2_offset_max are used to 
-            ## search harness from harnessList2 which contain "timing_type_set"
-            index2 = targetCell.out_ports.index(target_outport) 
-            index2_offset = 0
-            index2_offset_max = 10
-            while(index2_offset < index2_offset_max):
-                #print(harnessList2[index1][index2*2+index2_offset])
-                if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_reset"):
-                    break
-                index2_offset += 1
-            if(index2_offset == 10):
-                print("Error: index2_offset exceed max. search number\n")
-                exit()
-
-            outlines.append("      internal_power () {\n")
-            index2 = targetCell.in_ports.index(target_inport) 
-            outlines.append("        related_pin : \""+targetCell.reset+"\";\n")
-            ## rise(fall)
-            outlines.append("        "+harnesses[index1][index2*2].direction_power+" (power_template) {\n")
-            for lut_line in harnesses[index1][index2*2].lut_eintl:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            ## fall(rise)
-            outlines.append("        "+harnesses[index1][index2*2+1].direction_power+" (power_template) {\n")
-            for lut_line in harnesses[index1][index2*2+1].lut_eintl:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            outlines.append("      }\n") ## power end 
-
-        ## (3-6) set power 
-        if targetCell.set is not None:
-
-            ## Harness search for set
-            ## index2 is an base pointer for harness search
-            ## index2_offset and index2_offset_max are used to 
-            ## search harness from harnessList2 which contain "timing_type_set"
-            index2 = targetCell.out_ports.index(target_outport) 
-            index2_offset = 0
-            index2_offset_max = 10
-            while(index2_offset < index2_offset_max):
-                #print(harnessList2[index1][index2*2+index2_offset])
-                if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_set"):
-                    break
-                index2_offset += 1
-            if(index2_offset == 10):
-                print("Error: index2_offset exceed max. search number\n")
-                exit()
-
-            outlines.append("      internal_power () {\n")
-            index2 = targetCell.in_ports.index(target_inport) 
-            outlines.append("        related_pin : \""+targetCell.set+"\";\n")
-            ## rise(fall)
-            outlines.append("        "+harnesses[index1][index2*2].direction_power+" (power_template) {\n")
-            for lut_line in harnesses[index1][index2*2].lut_eintl:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            ## fall(rise)
-            outlines.append("        "+harnesses[index1][index2*2+1].direction_power+" (power_template) {\n")
-            for lut_line in harnesses[index1][index2*2+1].lut_eintl:
-                outlines.append("          "+lut_line+"\n")
-            outlines.append("        }\n") 
-            outlines.append("      }\n") ## power end 
-        ##outlines.append("    }\n") #### out pin end
-    outlines.append("    }\n") ## out pin end
-
-    ## (4) Reset
-    if targetCell.reset is not None:
-        # for target_reset in targetCell.reset:
-        target_reset = targetCell.reset
-        index1 = targetCell.reset.index(target_reset) 
-        outlines.append("    pin ("+target_reset+"){\n") #### out pin start
-        outlines.append("      direction : input;\n")
-        #outlines.append("      function : \"("+targetCell.functions[index1]+")\"\n")
-        outlines.append("      related_power_pin : \""+targetLib.vdd.name+"\";\n")
-        outlines.append("      related_ground_pin : \""+targetLib.vss.name+"\";\n")
-        outlines.append("      max_transition : "+str(targetCell.in_slopes[-1])+";\n")
-        ##outlines.append("      capacitance : \""+str(targetCell.crsts[index1])+"\";\n")
-        outlines.append("      capacitance : \""+str(targetCell.crsts[0])+"\";\n") # use 0 as representative
-        outlines.append("      input_voltage : default_"+targetLib.vdd.name+"_"+targetLib.vss.name+"_input;\n")
-        ##target_inport = targetCell.reset
-
-        ## Harness search for reset
-        ## index2 is an base pointer for harness search
-        ## index2_offset and index2_offset_max are used to 
-        ## search harness from harnessList2 which contain "timing_type_set"
-        index2 = targetCell.out_ports.index(target_outport) 
-        index2_offset = 0
-        index2_offset_max = 10
-        while(index2_offset < index2_offset_max):
-            #print(harnessList2[index1][index2*2+index2_offset])
-            if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_reset"):
-                break
-            index2_offset += 1
-        if(index2_offset == 10):
-            print("Error: index2_offset exceed max. search number\n")
-            exit()
-        #for a in inspect.getmembers(harnessList2[index1][index2*2+index2_offset]):
-        #	print (a) 
-        ## (4-1) recovery
-        outlines.append("      timing () {\n")
-        outlines.append("        related_pin : \""+targetCell.clock+"\";\n")
-        outlines.append("        timing_type : \""+harnesses[index1][index2*2+index2_offset].timing_type_reset_recov+"\";\n")
-        if (1 != len(targetCell.in_ports)):
-            print ("Error: number of targetCell.in_ports is "+str(len(targetCell.in_ports))+", not one! die" )
-            exit()
-        #for target_inport in targetCell.in_ports: ## only one target_inport should be available
-        #	outlines.append("        when : \""+harnessList2[index1][index2*2+index2_offset].timing_type_reset+"\";\n")
-        outlines.append("        "+harnesses[index1][index2*2+index2_offset].timing_sense_reset_recov+" (recovery_template) {\n")
-        for lut_line in harnesses[index1][index2*2+index2_offset].lut_setup:
-            outlines.append("          "+lut_line+"\n")
-        outlines.append("        }\n") 
-        outlines.append("      }\n") 
-        ## (4-2)removal 
-        outlines.append("      timing () {\n")
-        outlines.append("        related_pin : \""+targetCell.clock+"\";\n")
-        outlines.append("        timing_type : \""+harnesses[index1][index2*2+index2_offset].timing_type_reset_remov+"\";\n")
-        if (1 != len(targetCell.in_ports)):
-            print ("Error: number of targetCell.in_ports is "+str(len(targetCell.in_ports))+", not one! die" )
-            exit()
-        #for target_inport in targetCell.in_ports: ## only one target_inport should be available
-        #	outlines.append("        when : \""+harnessList2[index1][index2*2+index2_offset].timing_type_reset+"\";\n")
-        outlines.append("        "+harnesses[index1][index2*2+index2_offset].timing_sense_reset_remov+" (removal_template) {\n")
-        for lut_line in harnesses[index1][index2*2+index2_offset].lut_hold:
-            outlines.append("          "+lut_line+"\n")
-        outlines.append("        }\n") 
-        outlines.append("      }\n") 
-    outlines.append("    }\n") #### out pin end -> continue for set
-
-    ## (5) set
-    if targetCell.set is not None:
-        #for target_set in targetCell.set:
-        target_set = targetCell.set
-        index1 = targetCell.set.index(target_set) 
-        outlines.append("    pin ("+target_set+"){\n") #### out pin start
-        outlines.append("      direction : input;\n")
-        #outlines.append("      function : \"("+targetCell.functions[index1]+")\"\n")
-        outlines.append("      related_power_pin : \""+targetLib.vdd.name+"\";\n")
-        outlines.append("      related_ground_pin : \""+targetLib.vss.name+"\";\n")
-        outlines.append("      max_transition : "+str(targetCell.in_slopes[-1])+";\n")
-        #outlines.append("      capacitance : \""+str(targetCell.csets[index1])+"\";\n")
-        outlines.append("      capacitance : \""+str(targetCell.csets[0])+"\";\n") # use 0 as representative val
-        outlines.append("      input_voltage : default_"+targetLib.vdd.name+"_"+targetLib.vss.name+"_input;\n")
-        ##target_inport = targetCell.set
-
-        ## Harness search for set
-        ## index2 is an base pointer for harness search
-        ## index2_offset and index2_offset_max are used to 
-        ## search harness from harnessList2 which contain "timing_type_set"
-        index2 = targetCell.out_ports.index(target_outport) 
-        index2_offset = 0
-        index2_offset_max = 10
-        while(index2_offset < index2_offset_max):
-            #print(harnessList2[index1][index2*2+index2_offset])
-            if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_set"):
-                break
-            index2_offset += 1
-        if(index2_offset == 10):
-            print("Error: index2_offset exceed max. search number\n")
-            exit()
-
-        ## (5-1) recovery
-        outlines.append("      timing () {\n")
-        outlines.append("        related_pin : \""+targetCell.clock+"\";\n")
-        outlines.append("        timing_type : \""+harnesses[index1][index2*2+index2_offset].timing_type_set_recov+"\";\n")
-        if (1 != len(targetCell.in_ports)):
-            print ("Error: number of targetCell.in_ports is "+str(len(targetCell.in_ports))+", not one! die" )
-            exit()
-        #for target_inport in targetCell.in_ports: ## only one target_inport should be available
-        #	outlines.append("        when : \""+harnessList2[index1][index2*2+index2_offset].timing_type_set+"\";\n")
-        outlines.append("        "+harnesses[index1][index2*2+index2_offset].timing_sense_set_recov+" (recovery_template) {\n")
-        for lut_line in harnesses[index1][index2*2+index2_offset].lut_setup:
-            outlines.append("          "+lut_line+"\n")
-        outlines.append("        }\n") 
-        outlines.append("      }\n") 
-
-        ## (5-2)removal 
-        outlines.append("      timing () {\n")
-        outlines.append("        related_pin : \""+targetCell.clock+"\";\n")
-        outlines.append("        timing_type : \""+harnesses[index1][index2*2+index2_offset].timing_type_set_remov+"\";\n")
-        if (1 != len(targetCell.in_ports)):
-            print ("Error: number of targetCell.in_ports is "+str(len(targetCell.in_ports))+", not one! die" )
-            exit()
-        #for target_inport in targetCell.in_ports: ## only one target_inport should be available
-        #	outlines.append("        when : \""+harnessList2[index1][index2*2+index2_offset].timing_type_set+"\";\n")
-        outlines.append("        "+harnesses[index1][index2*2+index2_offset].timing_sense_set_remov+" (removal_template) {\n")
-        for lut_line in harnesses[index1][index2*2+index2_offset].lut_hold:
-            outlines.append("          "+lut_line+"\n")
-        outlines.append("        }\n") 
-        outlines.append("      }\n") 
-    outlines.append("    }\n") #### out pin end -> continue for set
-
-    outlines.append("  }\n") #### cell end
-    with open(targetLib.dotlib_name, 'a') as f:
-        f.writelines(outlines)
-        f.close()
-    targetCell.set_exported()
-
-
-## export library definition to .lib
-def exportVerilog(targetLib, targetCell):
-    with open(targetLib.verilog_name, 'a') as f:
-        outlines = []
-
-        ## list ports in one line 
-        portlist = "("
-        numport = 0
-        for target_outport in targetCell.out_ports:
-            if(numport != 0):
-                portlist = portlist+", "
-            portlist = portlist+target_outport
-            numport += 1
-        for target_inport in targetCell.in_ports:
-            portlist = portlist+","+target_inport
-            numport += 1
-        portlist = portlist+");"
-
-        outlines.append("module "+targetCell.name+portlist+"\n")
-
-        ## input/output statement
-        for target_outport in targetCell.out_ports:
-            outlines.append("output "+target_outport+";\n")
-        for target_inport in targetCell.in_ports:
-            outlines.append("input "+target_inport+";\n")
-
-        ## branch for sequencial cell
-        if(targetCell.logic == "DFFARAS"):
-            print ("This cell "+targetCell.logic+" is not supported for verilog out\n")
-            sys.exit
-
-        ## branch for combinational cell
-        else:
-        ## assign statement
-            for target_outport in targetCell.out_ports:
-                index1 = targetCell.out_ports.index(target_outport) 
-                outlines.append("assign "+target_outport+" = "+targetCell.functions[index1]+";\n")
-
-        outlines.append("endmodule\n\n")
-        f.writelines(outlines)
-    f.close()
-
-
-## export library definition to .lib
-def exportVerilogFlop(targetLib, targetCell):
-    with open(targetLib.verilog_name, 'a') as f:
-        outlines = []
-
-        ## list ports in one line 
-        portlist = "("
-        numport = 0
-        for target_outport in targetCell.out_ports:
-            if(numport != 0):
-                portlist = portlist+", "
-            portlist = portlist+target_outport
-            numport += 1
-        for target_inport in targetCell.in_ports:
-            portlist = portlist+","+target_inport
-            numport += 1
-        if targetCell.clock is not None:
-            portlist = portlist+","+targetCell.clock
-            numport += 1
-        if targetCell.reset is not None:
-            portlist = portlist+","+targetCell.reset
-            numport += 1
-        if targetCell.set is not None:
-            portlist = portlist+","+targetCell.set
-            numport += 1
-        portlist = portlist+");"
-
-        outlines.append("module "+targetCell.name+portlist+"\n")
-
-        ## input/output statement
-        for target_outport in targetCell.out_ports:
-            outlines.append("output "+target_outport+";\n")
-        for target_inport in targetCell.in_ports:
-            outlines.append("input "+target_inport+";\n")
-        if targetCell.clock is not None:
-            outlines.append("input "+targetCell.clock+";\n")
-        if targetCell.reset is not None:
-            outlines.append("input "+targetCell.reset+";\n")
-        if targetCell.set is not None:
-            outlines.append("input "+targetCell.set+";\n")
-
-        ## assign statement
-        for target_outport in targetCell.out_ports:
-            for target_inport in targetCell.in_ports:
-                line = 'always@('
-                resetlines = []
-                setlines = []
-                #print(str(targetCell.logic))
-                ## clock
-                if(re.search('PC', targetCell.logic)):	## posedge clock
-                    line=line+"posedge "+targetCell.clock
-                elif(re.search('NC', targetCell.logic)):	## negedge clock
-                    line=line+"negedge "+targetCell.clock
-                else:
-                    print("Error! failed to generate DFF verilog!")
-                    exit()
-
-                ## reset (option)
-                if(targetCell.reset != None):	## reset
-                    if(re.search('PR', targetCell.reset)):	## posedge async. reset
-                        line=line+" or posedge "+targetCell.reset
-                        resetlines.append('if('+targetCell.reset+')\n')
-                        resetlines.append('  '+target_outport+'<=0;\n')
-                        resetlines.append('else begin\n')
-                    elif(re.search('NR', targetCell.reset)):	## negedge async. reset
-                        line=str(line)+" or negedge "+targetCell.reset
-                        resetlines.append('if(!'+targetCell.reset+')\n')
-                        resetlines.append('  '+target_outport+'<=0;\n')
-                        resetlines.append('else begin\n')
-                ## set (option)
-                if(targetCell.set != None):	## reset
-                    if(re.search('PS', targetCell.set)):	## posedge async. set 
-                        line=line+" or posedge "+targetCell.set
-                        setlines.append('if('+targetCell.set+')begin\n')
-                        setlines.append('  '+target_outport+'<=1;\n')
-                        setlines.append('end\n')
-                        setlines.append('else begin\n')
-                    elif(re.search('NS', targetCell.set)):	## negedge async. set 
-                        line=line+" or negedge "+targetCell.set
-                        setlines.append('if(!'+targetCell.set+')begin\n')
-                        setlines.append('  '+target_outport+'<=1;\n')
-                        setlines.append('end\n')
-                        setlines.append('else begin\n')
-                line=line+")begin\n"
-                outlines.append(line)
-                if targetCell.set is not None:	
-                    outlines.append(setlines[0])
-                    outlines.append(setlines[1])
-                    outlines.append(setlines[2])
-                if targetCell.reset is not None:	
-                    outlines.append(resetlines[0])
-                    outlines.append(resetlines[1])
-                    outlines.append(resetlines[2])
-                outlines.append(target_outport+'<='+target_inport)
-                outlines.append('\nend\n')
-                outlines.append('end\n')
-            ## for target_inport
-        ## for target_outport
-        outlines.append("endmodule\n\n")
-        f.writelines(outlines)
-    f.close()
-
-
-## export harness data to .lib
-def exitFiles(targetLib, num_gen_file):
-    with open(targetLib.dotlib_name, 'a') as f:
-        outlines = []
-        outlines.append("}\n")
-        f.writelines(outlines)
-    f.close()
-    print("\n-- dotlib file generation completed!!  ")
-    print("--  "+str(num_gen_file)+" cells generated!!  \n")
diff --git a/characterizer/Harness.py b/characterizer/Harness.py
new file mode 100644
index 0000000..abcf446
--- /dev/null
+++ b/characterizer/Harness.py
@@ -0,0 +1,405 @@
+from liberty.UnitsSettings import EngineeringUnit
+
+class Harness:
+    """Characterization parameters for one path through a cell
+    
+    A Harness defines characterization parameters from one input to one
+    output of a standard cell circuit. The target input port and output
+    port are defined by the value of test_vector passed to the
+    constructor."""
+
+    def __init__(self, target_cell, test_vector: list) -> None:
+        """Create a new Harness.
+        
+        The key parameter here is test_vector. It describes the expected
+        state of each input pin and output pin, as well as which input and
+        output this Harness is testing. It should be formatted as a list of
+        strings with N+M entries, where N is the number of input ports and
+        M is the number of output ports in target_cell:
+
+        [in1, ..., inN, out1, ..., outM]
+
+        Each entry in test_vector is a string that represents the state of
+        the corresponding I/O on the standard cell. For inputs, the values
+        '0' and '1' represent nontarget input ports. These ports will be
+        held stable with the corresponding logic values. Values of '01' or
+        '10' indicate target input ports that are rising or falling
+        respectively. The case is similar for outputs: '0' and '1' represent
+        expected results for nontarget output ports, and '01' or '10'
+        represent target outputs that are expected to fall or rise as the
+        input changes.
+
+        For example, a test vector for a single-input single-output inverter
+        might look like this: ['01', '10'].
+        Or a test vector for an AND gate with 2 inputs and a single output
+        might look like: ['01', '1', '01'].
+
+        Note that target_cell is only required for initialization checks,
+        such as ensuring that test_vector correctly maps to the input and
+        output pins of the cell under test. A Harness does not keep track of
+        changes to target_cell. If target_cell is altered after the Harness
+        was generated, a new Harness should be generated."""
+
+        self._stable_in_ports = []              # input pins to hold stable
+        self._stable_in_port_states = []        # states for stable pins
+        self._nontarget_out_ports = []          # output pins that we aren't specifically evaluating
+        self._nontarget_out_port_states = []    # states for nontarget outputs
+        self.results =  {}                      # nested dictionary of characterization results grouped by in_slew and out_load
+
+        # Parse inputs from test vector
+        # Test vector should be formatted like [in1, ..., inN, out1, ..., outM]
+        num_inputs = len(target_cell.in_ports)
+        num_outputs = len(target_cell.out_ports)
+        input_test_vector = test_vector[0:num_inputs]
+        output_test_vector = test_vector[num_inputs:num_inputs+num_outputs]
+
+        # Get inputs from test vector
+        for in_port, state in zip(target_cell.in_ports, input_test_vector):
+            if len(state) > 1:
+                self._target_in_port = in_port
+                self._target_in_port_state = state
+            else:
+                self._stable_in_ports.append(in_port)
+                self._stable_in_port_states.append(state)
+
+        # Get outputs from test vector
+        for out_port, state in zip(target_cell.out_ports, output_test_vector):
+            if len(state) > 1:
+                self._target_out_port = out_port
+                self._target_out_port_state = state
+            else:
+                self._nontarget_out_ports.append(out_port)
+                self._nontarget_out_port_states.append(state)
+        if not self._target_out_port:
+            raise ValueError(f'Unable to parse target output port from test vector {test_vector}')
+
+        # Initialize results from target_cell input slopes and loads
+        for in_slew in target_cell.in_slews:
+            self.results[str(in_slew)] = {}
+            for out_load in target_cell.out_loads:
+                self.results[str(in_slew)][str(out_load)] = {}
+
+    def __str__(self) -> str:
+        lines = []
+        lines.append(f'Arc Under Test: {self.target_in_port} ({self.in_direction}) -> {self.target_out_port} ({self.out_direction})')
+        if self.stable_in_ports:
+            lines.append(f'    Stable Input Ports:')
+            for port, state in zip(self.stable_in_ports, self.stable_in_port_states):
+                lines.append(f'        {port}: {state}')
+        if self.nontarget_out_ports:
+            lines.append(f'    Nontarget Output Ports:')
+            for port, state in zip(self.nontarget_out_ports, self.nontarget_out_port_states):
+                lines.append(f'        {port}: {state}')
+        # TODO: Display results if available
+        return '\n'.join(lines)
+
+    def _state_to_direction(self, state) -> str:
+        return 'rise' if state == '01' else 'fall' if state == '10' else None
+
+    @property
+    def target_in_port(self) -> str:
+        return self._target_in_port
+
+    @property
+    def target_in_port_state(self) -> str:
+        return self._target_in_port_state
+
+    @property
+    def stable_in_ports(self) -> list:
+        return self._stable_in_ports
+
+    @property
+    def stable_in_port_states(self) -> list:
+        return self._stable_in_port_states
+
+    @property
+    def target_out_port(self) -> str:
+        return self._target_out_port
+
+    @property
+    def target_out_port_state(self) -> str:
+        return self._target_out_port_state
+
+    @property
+    def nontarget_out_ports(self) -> list:
+        return self._nontarget_out_ports
+
+    @property
+    def nontarget_out_port_states(self) -> list:
+        return self._nontarget_out_port_states
+
+    @property
+    def in_direction(self) -> str:
+        return self._state_to_direction(self.target_in_port_state)
+
+    @property
+    def out_direction(self) -> str:
+        return self._state_to_direction(self.target_out_port_state)
+
+    @property
+    def direction_prop(self) -> str:
+        return f'cell_{self.out_direction}'
+
+    @property
+    def direction_tran(self) -> str:
+        return f'{self.out_direction}_transition'
+
+    @property
+    def direction_power(self) -> str:
+        return f'{self.out_direction}_power'
+
+    @property
+    def timing_sense(self) -> str:
+        # Determine timing_sense from target i/o directions
+        if self.in_direction == self.out_direction:
+            return 'positive_unate'
+        else:
+            return 'negative_unate'
+
+    def average_input_capacitance(self, vdd_voltage, capacitance_unit: EngineeringUnit) -> float:
+        input_capacitance = 0
+        n = 0
+        for slope in self.results.keys():
+            for load in self.results[slope].keys():
+                # TODO: Correct for cases where q is negative
+                q = self.results[slope][load]['q_in_dyn']
+                input_capacitance += q / vdd_voltage
+                n += 1
+        input_capacitance = input_capacitance / (n * capacitance_unit.magnitude)
+        return input_capacitance
+
+    def average_transition_delay(self) -> float:
+        total_delay = 0
+        n = 0
+        for slope in self.results.keys():
+            for load in self.results[slope].keys():
+                total_delay += self.results[slope][load]['trans_out']
+                n += 1
+        return total_delay/n
+    
+    def average_propagation_delay(self) -> float:
+        total_delay = 0
+        n = 0
+        for slope in self.results.keys():
+            for load in self.results[slope].keys():
+                total_delay += self.results[slope][load]['prop_in_out']
+                n += 1
+        return total_delay / n
+
+    def _calc_leakage_power(self, in_slew, out_load, vdd_voltage: float):
+        i_vdd_leak = abs(self.results[in_slew][out_load]['i_vdd_leak'])
+        i_vss_leak = abs(self.results[in_slew][out_load]['i_vss_leak'])
+        avg_current = (i_vdd_leak + i_vss_leak) / 2
+        return avg_current * vdd_voltage
+    
+    def get_leakage_power(self, vdd_voltage, power_unit: EngineeringUnit):
+        leakage_power = 0
+        n = 0
+        for slope in self.results.keys():
+            for load in self.results[slope].keys():
+                leakage_power += self._calc_leakage_power(slope, load, vdd_voltage)
+                n += 1
+        leakage_power = leakage_power / (n * power_unit.magnitude)
+        return leakage_power
+
+    def _get_lut_value_groups_by_key(self, in_slews, out_loads, unit, key: str):
+        value_groups = []
+        for slew in in_slews:
+            values = [self.results[str(slew)][str(load)][key]/unit for load in out_loads]
+            value_groups.append(f'"{", ".join([f"{value:f}" for value in values])}"')
+        sep = ', \\\n  '
+        return f'values( \\\n  {sep.join(value_groups)});'
+
+    def get_propagation_delay_lut(self, in_slews, out_loads, time_unit: EngineeringUnit) -> list:
+        lines = [f'index_1("{", ".join([str(slew) for slew in in_slews])}");']
+        lines.append(f'index_2("{", ".join([str(load) for load in out_loads])}");')
+        values = self._get_lut_value_groups_by_key(in_slews, out_loads, time_unit.magnitude, 'prop_in_out')
+        [lines.append(value_line) for value_line in values.split('\n')]
+        return lines
+
+    def get_transport_delay_lut(self, in_slews, out_loads, time_unit: EngineeringUnit):
+        lines = [f'index_1("{", ".join([str(slew) for slew in in_slews])}");']
+        lines.append(f'index_2("{", ".join([str(load) for load in out_loads])}");')
+        values = self._get_lut_value_groups_by_key(in_slews, out_loads, time_unit.magnitude, 'trans_out')
+        [lines.append(value_line) for value_line in values.split('\n')]
+        return lines
+
+    def _calc_internal_energy(self, in_slew: str, out_load: str, energy_meas_high_threshold_voltage: float, energy_unit: EngineeringUnit, current_unit: EngineeringUnit):
+        """Calculates internal energy for a particular slope/load combination"""
+        # Fetch calculation parameters
+        e_start = self.results[in_slew][out_load]['energy_start']
+        e_end = self.results[in_slew][out_load]['energy_end']
+        q_vdd_dyn = self.results[in_slew][out_load]['q_vdd_dyn']
+        q_vss_dyn = self.results[in_slew][out_load]['q_vss_dyn']
+        i_vdd_leak = abs(self.results[in_slew][out_load]['i_vdd_leak'])
+        i_vss_leak = abs(self.results[in_slew][out_load]['i_vss_leak'])
+        # Perform the calculation
+        energy_delta = (e_end - e_start)
+        avg_current = (i_vdd_leak + i_vss_leak) / 2
+        internal_charge = min(abs(q_vss_dyn), abs(q_vdd_dyn)) - energy_delta * avg_current
+        return internal_charge * energy_meas_high_threshold_voltage /energy_unit.magnitude
+
+    def get_internal_energy_lut(self, in_slews, out_loads, v_eth: float, e_unit: EngineeringUnit, i_unit: EngineeringUnit):
+        lines = [f'index_1("{", ".join([str(slope) for slope in in_slews])}");']
+        lines.append(f'index_2("{", ".join([str(load) for load in out_loads])}");')
+        energy_groups = []
+        for slew in in_slews:
+            energies = [self._calc_internal_energy(str(slew), str(out_load), v_eth, e_unit, i_unit) for out_load in out_loads]
+            energy_groups.append(f'"{", ".join(["{:f}".format(energy) for energy in energies])}"')
+        sep = ', \\\n  '
+        [lines.append(value_line) for value_line in f'values( \\\n  {sep.join(energy_groups)});'.split('\n')]
+        return lines
+
+class CombinationalHarness (Harness):
+    def __init__(self, target_cell, test_vector) -> None:
+        super().__init__(target_cell, test_vector)
+        # Error if we don't have a target input port
+        if not self._target_in_port:
+            raise ValueError(f'Unable to parse target input port from test vector {test_vector}')
+
+    def spice_infix(self):
+        # Determine the infix for spice files dealing with this harness
+        infix = f'{self.target_in_port}{"01" if self.in_direction == "rise" else "10"}'
+        for input, state in zip(self.stable_in_ports, self.stable_in_port_states):
+            infix += f'_{input}{state}'
+        infix += f'_{self.target_out_port}{"01" if self.out_direction == "rise" else "10"}'
+        for output, state in zip(self.nontarget_out_ports, self.nontarget_out_port_states):
+            infix += f'_{output}{state}'
+        return infix
+
+    @property
+    def timing_type(self) -> str:
+        return "combinational"
+
+
+class SequentialHarness (Harness):
+    def __init__(self, target_cell, test_vector: list) -> None:
+        # Parse internal storage states, clock, set, and reset out of test vector
+        # For sequential harnesses, test vectors are in the format:
+        # [clk, set, reset, flop1, ..., flopK, in1, ..., inN, out1, ..., outM]
+        # Note that set and reset are optional, but must be provided if present
+        # on the target cell
+        self.set = None
+        self.reset = None
+        self.flops = []
+        self.flop_states = []
+        self.clock = target_cell.clock
+        self.clock_state = test_vector.pop(0)
+        # Set up Reset
+        if target_cell.reset:
+            self.reset = target_cell.reset
+            self.reset_state = test_vector.pop(0)
+            if len(self.reset_state) > 1:
+                self._target_in_port = self.reset
+        # Set up Set
+        if target_cell.set:
+            self.set = target_cell.set
+            self.set_state = test_vector.pop(0)
+            if len(self.set_state) > 1:
+                # Overwrite reset if already assigned
+                self._target_in_port = self.set
+        # Set up flop internal states
+        for flop in target_cell.flops:
+            self.flops.append(flop)
+            self.flop_states.append(test_vector.pop(0))
+        super().__init__(target_cell, test_vector)
+
+    @property
+    def set_direction(self) -> str:
+        if not self.set:
+            return None
+        return self._state_to_direction(self.set_state)
+
+    @property
+    def reset_direction(self) -> str:
+        if not self.reset:
+            return None
+        return self._state_to_direction(self.reset_state)
+    
+    def invert_set_reset(self):
+        self.set_state = self.set_state[::-1] if self.set_state else None
+        self.reset_state = self.reset_state[::-1] if self.reset_state else None
+
+    @property
+    def timing_sense_constraint(self) -> str:
+        # TODO: Check that this is correct
+        return f'{self.in_direction}_constraint'
+
+    def _timing_type_with_mode(self, mode) -> str:
+        # Determine from target input and direction
+        if self.set_direction or self.reset_direction:
+            # We're targeting set or reset
+            if mode == 'recovery':
+                if self.in_direction == 'rise':
+                    return f'{mode}_rising'
+                else:
+                    return f'{mode}_falling'
+            elif mode == 'removal':
+                if self.in_direction == 'rise':
+                    return f'{mode}_falling'
+                else:
+                    return f'{mode}_rising'
+            else:
+                return None
+        elif not self.target_in_port in [*self.flops]:
+            # We're targeting an input port
+            if mode == 'clock':
+                if self.clock_state == '0101':
+                    return 'falling_edge'
+                else:
+                    return 'rising_edge'
+            elif mode in ['hold', 'setup']:
+                if self.in_direction == 'rise':
+                    return f'{mode}_rising'
+                else:
+                    return f'{mode}_falling'
+        # If we get here, most likely the harness isn't configured correctly
+        raise ValueError(f'Unable to determine timing type for mode "{mode}"')
+
+    @property
+    def timing_type_hold(self) -> str:
+        return self._timing_type_with_mode('hold')
+
+    @property
+    def timing_type_setup(self) -> str:
+        return self._timing_type_with_mode('setup')
+    
+    @property
+    def timing_type_recovery(self) -> str:
+        return self._timing_type_with_mode('recovery')
+    
+    @property
+    def timing_type_removal(self) -> str:
+        return self._timing_type_with_mode('removal')
+    
+    @property
+    def timing_type_clock(self) -> str:
+        return self._timing_type_with_mode('clock')
+    
+    @property
+    def timing_when(self) -> str:
+        if self.in_direction == 'rise':
+            return self.target_in_port
+        else:
+            return f'!{self.target_in_port}'
+
+
+# Utilities for working with Harnesses
+def filter_harnesses_by_ports(harness_list: list, in_port, out_port) -> list:
+    """Finds harnesses in harness_list which target in_port and out_port"""
+    return [harness for harness in harness_list if harness.target_in_port == in_port and harness.target_out_port == out_port]
+
+def find_harness_by_arc(harness_list: list, in_port, out_port, out_direction) -> Harness:
+    harnesses = [harness for harness in filter_harnesses_by_ports(harness_list, in_port, out_port) if harness.out_direction == out_direction]
+    if len(harnesses) > 1:
+        raise LookupError('Multiple harnesses present in harness_list with the specified arc!')
+    elif len(harnesses) < 1:
+        raise LookupError('No harnesses present in harness_list with the specified arc!')
+    return harnesses[0]
+
+def check_timing_sense(harness_list: list):
+    """Checks that all CombinationalHarnesses in harness_list have the same unateness."""
+    for harness in harness_list:
+        if not harness.timing_sense == harness_list[0].timing_sense:
+            return "non_unate"
+    return harness_list[0].timing_sense
\ No newline at end of file
diff --git a/characterizer/HarnessSettings.py b/characterizer/HarnessSettings.py
deleted file mode 100644
index 02f8988..0000000
--- a/characterizer/HarnessSettings.py
+++ /dev/null
@@ -1,629 +0,0 @@
-class HarnessSettings:
-    def __init__ (self):
-        self.target_inport = None     ## target inport name
-        self.target_outport = None    ## target outport name
-        self.stable_inport = []       ## stable imports
-        self.stable_inport_val = []   ## stable imports val
-        self.nontarget_outport = []   ## nontarget outport
-        self.target_clock     = None  ## target clock name
-        self.target_clock_val = None  ## target clock value
-        self.target_reset     = None  ## target reset name
-        self.target_reset_val = None  ## target reset val
-        self.target_set     = None    ## target set name
-        self.target_set_val = None    ## target set val
-    
-    def set_direction(self, outport="tmp"):
-        if(outport == '01'):
-            self.set_direction_rise()
-        elif(outport == '10'):
-            self.set_direction_fall()
-        else:
-            print("Illegal input: " + self.outport + ", check direction")
-
-    def set_direction_rise(self):
-        self.direction_prop = "cell_rise"
-        self.direction_tran = "rise_transition"
-        self.direction_power = "rise_power"
-
-    def set_direction_fall(self):
-        self.direction_prop = "cell_fall"
-        self.direction_tran = "fall_transition"
-        self.direction_power = "fall_power"
-
-    def set_timing_type_comb(self):
-        self.timing_type = "combinational"
-
-    def set_timing_sense(self, unate="tmp"):
-        if(unate == 'pos'):
-            self.timing_sense = "positive_unate"
-        elif(unate == 'neg'):
-            self.timing_sense = "negative_unate"
-        elif(unate == 'non'):
-            self.timing_sense = "non_unate"
-        else:
-            print("Illegal input: "+self.outport+", check unate")
-
-    def set_timing_sense_pos(self):
-        self.timing_sense = "positive_unate"
-
-    def set_timing_sense_neg(self):
-        self.timing_sense = "negative_unate"
-
-    ## set timing_sense and timing_type for input/output
-    def set_timing_flop_inout(self, inport="tmp", clkport="tmp",outport="tmp"):
-        ## inport
-        if((inport == 'pos')or(inport == '01')):
-            self.timing_sense_setup = "rise_constraint"
-            self.timing_sense_hold  = "rise_constraint"
-            self.timing_type_setup = "setup_rising"
-            self.timing_type_hold  = "hold_rising"
-        elif((inport == 'neg')or(inport == '10')):
-            self.timing_sense_setup = "fall_constraint"
-            self.timing_sense_hold  = "fall_constraint"
-            self.timing_type_setup = "setup_falling"
-            self.timing_type_hold  = "hold_falling"
-        else:
-            print("Illegal input: "+inport+", check unate")
-        ## outport
-        if((outport == 'pos')or(outport == '01')):
-            self.timing_type_out = "rising_edge"
-        elif((outport == 'neg')or(outport == '10')):
-            self.timing_type_out = "falling_edge"
-        else:
-            print("Illegal input: "+outport+", check unate")
-        ## clkport
-        self.timing_sense_clock = "non_unate"
-        if(clkport == '0101'):
-            #self.timing_type_clock = "setup_rising"
-            self.timing_type_clock = "rising_edge"
-        elif(clkport == '1010'):
-            #self.timing_type_clock = "setup_falling"
-            self.timing_type_clock = "falling_edge"
-        else:
-            print("Illegal clkput: "+clkport+", check unate")
-    
-#	def set_timing_flop_clock(self, inport="0101", outport="01"):
-#		## inport
-#		self.timing_sense_clock  = "non_unate"
-#		if(inport == "0101"):
-#			self.timing_type_clock = "rising_edge"
-#		elif(inport == "1010"):
-#			self.timing_type_clock = "falling_edge"
-#		else:
-#			self.timing_type_clock  = "NONE"
-#		if((outport == "01")or(outport.upper() == "RISE")):
-#			self.direction_clock_prop = "cell_rise"
-#			self.direction_clock_tran = "rise_transition"
-#		elif((outport == "10")or(outport.upper() == "FALL")):
-#			self.direction_clock_prop = "cell_fall"
-#			self.direction_clock_tran = "fall_transition"
-#		else:
-#			print("Warning: illegal outport type at set_timing_flop_set: "+str(inport))
-
-    def set_timing_flop_set(self, targetCell, inport="01", outport="01"):
-        ## inport
-        if((inport == "01")or(inport.upper() == "RISE")):
-            self.timing_sense_set = "positive_unate"
-            self.timing_when = targetCell.set 
-            self.timing_sense_set_recov = "rise_constraint"
-            self.timing_sense_set_remov = "rise_constraint"
-        elif((inport == "10")or(inport.upper() == "FALL")):
-            self.timing_sense_set = "negative_unate"
-            self.timing_when = "!"+targetCell.set 
-            self.timing_sense_set_recov = "fall_constraint"
-            self.timing_sense_set_remov = "fall_constraint"
-        else:
-            print("Warning: illegal inport type at set_timing_flop_set: "+str(inport))
-        ## outport
-        if((outport == "01")or(outport.upper() == "RISE")):
-            self.timing_type_set = "preset"
-            self.direction_set_prop = "cell_rise"
-            self.direction_set_tran = "rise_transition"
-            self.timing_type_set_recov = "recovery_rising"
-            self.timing_type_set_remov = "removal_falling"
-        elif((outport == "10")or(outport.upper() == "FALL")):
-            self.timing_type_set = "clear"
-            self.direction_set_prop = "cell_fall"
-            self.direction_set_tran = "fall_transition"
-            self.timing_type_set_recov = "recovery_falling"
-            self.timing_type_set_remov = "removal_rising"
-        else:
-            print("Warning: illegal outport type at set_timing_flop_set: "+str(inport))
-
-    def set_timing_flop_reset(self, targetCell, inport="01", outport="01"):
-        ## inport
-        if((inport == "01")or(inport.upper() == "RISE")):
-            self.timing_sense_reset = "positive_unate"
-            self.timing_when = targetCell.reset 
-            self.timing_sense_reset_recov = "rise_constraint"
-            self.timing_sense_reset_remov = "rise_constraint"
-        elif((inport == "10")or(inport.upper() == "FALL")):
-            self.timing_sense_reset = "negative_unate"
-            self.timing_when = "!"+targetCell.reset 
-            self.timing_sense_reset_recov = "fall_constraint"
-            self.timing_sense_reset_remov = "fall_constraint"
-        else:
-            print("Warning: illegal inport type at set_timing_flop_reset: "+str(inport))
-        ## outport
-        if((outport == "01")or(outport.upper() == "RISE")):
-            self.timing_type_reset = "preset"
-            self.direction_reset_prop = "cell_rise"
-            self.direction_reset_tran = "rise_transition"
-            self.timing_type_reset_recov = "recovery_rising"
-            self.timing_type_reset_remov = "removal_falling"
-        elif((outport == "10")or(outport.upper() == "FALL")):
-            self.timing_type_reset = "clear"
-            self.direction_reset_prop = "cell_fall"
-            self.direction_reset_tran = "fall_transition"
-            self.timing_type_reset_recov = "recovery_falling"
-            self.timing_type_reset_remov = "removal_rising"
-        else:
-            print("Warning: illegal outport type at set_timing_flop_reset: "+str(inport))
-
-    def set_function(self, function="tmp"):
-        self.function = function 
-
-    def set_target_inport(self, inport="tmp", val="01"):
-        self.target_inport = inport
-        self.target_inport_val = val
-        #print(self.target_inport_val)
-        #print(self.target_inport)
-
-    def set_target_outport(self, outport="tmp", function="tmp", val="01"):
-        self.target_outport = outport
-        self.target_function = function
-        self.target_outport_val = val
-        #print(self.target_outport_val)
-        #print(self.target_outport)
-        #print(self.target_function)
-
-    def set_stable_inport(self, inport="tmp", val="1"):
-        self.stable_inport.append(inport)
-        self.stable_inport_val.append(val)
-        #print(self.stable_inport)
-        #print(self.stable_inport_val)
-
-    def set_nontarget_outport(self, outport="tmp"):
-        self.stable_nontarget.append(outport)
-        #print(self.outport)
-
-    def set_target_clock(self, inport="tmp", val="01"):
-        self.target_clock = inport
-        self.target_clock_val = val
-        #print(self.target_clock_val)
-        #print(self.target_clock)
-
-    def set_target_reset(self, inport="tmp", val="01"):
-        self.target_reset = inport
-        self.target_reset_val = val
-        #print(self.target_reset_val)
-        #print(self.target_reset)
-
-    def set_target_set(self, inport="tmp", val="01"):
-        self.target_set = inport
-        self.target_set_val = val
-        #print(self.target_set_val)
-        #print(self.target_set)
-
-    def invert_set_reset_val(self):
-        if(self.target_reset_val == '01'):
-            self.target_reset_val = '10'
-        elif(self.target_reset_val == '10'):
-            self.target_reset_val = '01'
-        if(self.target_set_val == '01'):
-            self.target_set_val = '10'
-        elif(self.target_set_val == '10'):
-            self.target_set_val = '01'
-
-    ## propagation delay table
-    def set_list2_prop(self, list2_prop=[]):
-        self.list2_prop = list2_prop 
-
-    def print_list2_prop(self, ilist, jlist):
-        for i in range(len(ilist)):
-            for j in range(len(jlist)):
-                print(self.list2_prop[i][j])
-    
-    def print_lut_prop(self):
-        for i in range(len(self.lut_prop)):
-            print(self.lut_prop[i])
-
-    def write_list2_prop(self, targetLib, ilist, jlist):
-        ## index_1
-        outline = "index_1(\""
-        self.lut_prop = []
-        for j in range(len(jlist)-1):
-            outline += str(jlist[j])+", " 
-        outline += str(jlist[len(jlist)-1])+"\");" 
-        #print(outline)
-        self.lut_prop.append(outline)
-        ## index_2
-        outline = "index_2(\""
-        for i in range(len(ilist)-1):
-            outline += str(ilist[i])+", " 
-        outline += str(ilist[len(ilist)-1])+"\");" 
-        self.lut_prop.append(outline)
-        ## values
-        self.lut_prop.append("values ( \\")
-        for i in range(len(ilist)):
-            outline = "\""
-            for j in range(len(jlist)-1):
-                #outline += str(self.list2_prop[i][j])+", "
-                tmp_line = str("{:5f}".format(self.list2_prop[i][j]/targetLib.units.time.magnitude))
-                outline += tmp_line+", "
-            ## do not add "," for last line
-            if(i == (len(ilist)-1)): 
-                #outline += str(self.list2_prop[i][len(jlist)-1])+"\" \\"
-                tmp_line = str("{:5f}".format(self.list2_prop[i][len(jlist)-1]/targetLib.units.time.magnitude))
-                outline += tmp_line+"\" \\"
-            ##  add "," for else 
-            else:	
-                #outline += str(self.list2_prop[i][len(jlist)-1])+"\", \\"
-                tmp_line = str("{:5f}".format(self.list2_prop[i][len(jlist)-1]/targetLib.units.time.magnitude))
-                outline += tmp_line+"\", \\"
-            self.lut_prop.append(outline)
-        self.lut_prop.append(");")
-
-    ## transient delay table
-    def set_list2_tran(self, list2_tran=[]):
-        self.list2_tran = list2_tran 
-
-    def print_list2_tran(self, ilist, jlist):
-        for i in range(len(ilist)):
-            for j in range(len(jlist)):
-                print(self.list2_tran[i][j])
-    
-    def print_lut_tran(self):
-        for i in range(len(self.lut_tran)):
-            print(self.lut_tran[i])
-
-    def write_list2_tran(self, targetLib, ilist, jlist):
-        ## index_1
-        outline = "index_1(\""
-        self.lut_tran = []
-        for j in range(len(jlist)-1):
-            outline += str(jlist[j])+", " 
-        outline += str(jlist[len(jlist)-1])+"\");" 
-        #print(outline)
-        self.lut_tran.append(outline)
-        ## index_2
-        outline = "index_2(\""
-        for i in range(len(ilist)-1):
-            outline += str(ilist[i])+", " 
-        outline += str(ilist[len(ilist)-1])+"\");" 
-        self.lut_tran.append(outline)
-        ## values
-        self.lut_tran.append("values ( \\")
-        for i in range(len(ilist)):
-            outline = "\""
-            for j in range(len(jlist)-1):
-                #outline += str(self.list2_tran[i][j])+", "
-                tmp_line = str("{:5f}".format(self.list2_tran[i][j]/targetLib.units.time.magnitude))
-                outline += tmp_line+", "
-            if(i == (len(ilist)-1)): 
-                #outline += str(self.list2_tran[i][len(jlist)-1])+"\" \\"
-                tmp_line = str("{:5f}".format(self.list2_tran[i][len(jlist)-1]/targetLib.units.time.magnitude))
-                outline += tmp_line+"\" \\"
-            ##  add "," for else 
-            else:	
-                #outline += str(self.list2_tran[i][len(jlist)-1])+"\", \\"
-                tmp_line = str("{:5f}".format(self.list2_prop[i][len(jlist)-1]/targetLib.units.time.magnitude))
-                outline += tmp_line+"\", \\"
-            self.lut_tran.append(outline)
-        self.lut_tran.append(");")
-
-    ## internal power (energy) table 
-    def set_list2_eintl(self, list2_eintl=[]):
-        self.list2_eintl = list2_eintl 
-
-    def print_list2_eintl(self, ilist, jlist):
-        for i in range(len(ilist)):
-            for j in range(len(jlist)):
-                print(self.list2_eintl[i][j])
-    
-    def print_lut_eintl(self):
-        for i in range(len(self.lut_eintl)):
-            print(self.lut_eintl[i])
-
-    def write_list2_eintl(self, targetLib, ilist, jlist):
-        ## index_1
-        outline = "index_1(\""
-        self.lut_eintl = []
-        for j in range(len(jlist)-1):
-            outline += str(jlist[j])+", " 
-        outline += str(jlist[len(jlist)-1])+"\");" 
-        #print(outline)
-        self.lut_eintl.append(outline)
-        ## index_2
-        outline = "index_2(\""
-        for i in range(len(ilist)-1):
-            outline += str(ilist[i])+", " 
-        outline += str(ilist[len(ilist)-1])+"\");" 
-        self.lut_eintl.append(outline)
-        ## values
-        self.lut_eintl.append("values ( \\")
-        for i in range(len(ilist)):
-            outline = "\""
-            for j in range(len(jlist)-1):
-                #outline += str(self.list2_eintl[i][j])+", "
-                tmp_line = str("{:5f}".format(self.list2_eintl[i][j]/targetLib.units.voltage.magnitude/targetLib.units.energy.magnitude))
-                outline += tmp_line+", "
-            if(i == (len(ilist)-1)): 
-                #outline += str(self.list2_eintl[i][len(jlist)-1])+"\" \\"
-                tmp_line = str("{:5f}".format(self.list2_eintl[i][len(jlist)-1]/targetLib.units.voltage.magnitude/targetLib.units.energy.magnitude))
-                outline += tmp_line+"\" \\"
-            ##  add "," for else 
-            else:	
-                #outline += str(self.list2_eintl[i][len(jlist)-1])+"\", \\"
-                tmp_line = str("{:5f}".format(self.list2_eintl[i][len(jlist)-1]/targetLib.units.voltage.magnitude/targetLib.units.energy.magnitude))
-                outline += tmp_line+"\" \\"
-            self.lut_eintl.append(outline)
-        self.lut_eintl.append(");")
-
-    ## input energy 
-    def set_list2_ein(self, list2_ein=[]):
-        self.list2_ein = list2_ein 
-
-    def print_list2_ein(self, ilist, jlist):
-        for i in range(len(ilist)):
-            for j in range(len(jlist)):
-                print(self.list2_ein[i][j])
-    
-    def print_lut_ein(self):
-        for i in range(len(self.lut_ein)):
-            print(self.lut_ein[i])
-
-    def write_list2_ein(self, targetLib, ilist, jlist):
-        ## index_1
-        outline = "index_1(\""
-        self.lut_ein = []
-        for j in range(len(jlist)-1):
-            outline += str(jlist[j])+", " 
-        outline += str(jlist[len(jlist)-1])+"\");" 
-        #print(outline)
-        self.lut_ein.append(outline)
-        ## index_2
-        outline = "index_2(\""
-        for i in range(len(ilist)-1):
-            outline += str(ilist[i])+", " 
-        outline += str(ilist[len(ilist)-1])+"\");" 
-        self.lut_ein.append(outline)
-        ## values
-        self.lut_ein.append("values ( \\")
-        for i in range(len(ilist)):
-            outline = "\""
-            for j in range(len(jlist)-1):
-                #outline += str(self.list2_ein[i][j])+", "
-                tmp_line = str("{:5f}".format(self.list2_ein[i][j]/targetLib.units.voltage.magnitude/targetLib.units.energy.magnitude))
-                outline += tmp_line+", "
-            if(i == (len(ilist)-1)): 
-                #outline += str(self.list2_ein[i][len(jlist)-1])+"\" \\"
-                tmp_line = str("{:5f}".format(self.list2_ein[i][len(jlist)-1]/targetLib.units.voltage.magnitude/targetLib.units.energy.magnitude))
-                outline += tmp_line+"\" \\"
-            ##  add "," for else 
-            else:	
-                #outline += str(self.list2_ein[i][len(jlist)-1])+"\", \\"
-                tmp_line = str("{:5f}".format(self.list2_ein[i][len(jlist)-1]/targetLib.units.voltage.magnitude/targetLib.units.energy.magnitude))
-                outline += tmp_line+"\", \\"
-            self.lut_ein.append(outline)
-        self.lut_ein.append(");")
-
-    ## input capacitance 
-    def set_list2_cin(self, list2_cin=[]):
-        self.list2_cin = list2_cin 
-
-    def print_list2_cin(self, ilist, jlist):
-        for i in range(len(ilist)):
-            for j in range(len(jlist)):
-                print(self.list2_cin[i][j])
-    
-    def print_lut_cin(self):
-        for i in range(len(self.lut_cin)):
-            print(self.lut_cin[i])
-
-    def average_list2_cin(self, targetLib, ilist, jlist):
-        ## output average of input capacitance
-        ## (do not write table)
-        self.lut_cin = 0
-        for i in range(len(ilist)):
-            for j in range(len(jlist)):
-                self.lut_cin += self.list2_cin[i][j]
-        #self.cin = str(self.lut_cin / (len(ilist) * len(jlist))/targetLib.units.capacitance.magnitude) ## use average
-        self.cin = str(self.lut_cin / (len(ilist) * len(jlist))) ## use average
-        #print("store cin:"+str(self.cin))
-
-    ## clock input energy 
-    def set_list2_eclk(self, list2_eclk=[]):
-        self.list2_eclk = list2_eclk 
-
-    def print_list2_eclk(self, ilist, jlist):
-        for i in range(len(ilist)):
-            for j in range(len(jlist)):
-                print(self.list2_eclk[i][j])
-    
-    def print_lut_eclk(self):
-        for i in range(len(self.lut_eclk)):
-            print(self.lut_eclk[i])
-
-    def write_list2_eclk(self, targetLib, ilist, jlist):
-        ## index_1
-        outline = "index_1(\""
-        self.lut_eclk = []
-        for j in range(len(jlist)-1):
-            outline += str(jlist[j])+", " 
-        outline += str(jlist[len(jlist)-1])+"\");" 
-        #print(outline)
-        self.lut_eclk.append(outline)
-        ## index_2
-        outline = "index_2(\""
-        for i in range(len(ilist)-1):
-            outline += str(ilist[i])+", " 
-        outline += str(ilist[len(ilist)-1])+"\");" 
-        self.lut_eclk.append(outline)
-        ## values
-        self.lut_eclk.append("values ( \\")
-        for i in range(len(ilist)):
-            outline = "\""
-            for j in range(len(jlist)-1):
-                #outline += str(self.list2_eclk[i][j])+", "
-                tmp_line = str("{:5f}".format(self.list2_eclk[i][j]/targetLib.units.voltage.magnitude/targetLib.units.energy.magnitude))
-                outline += tmp_line+", "
-            if(i == (len(ilist)-1)): 
-                #outline += str(self.list2_eclk[i][len(jlist)-1])+"\" \\"
-                tmp_line = str("{:5f}".format(self.list2_eclk[i][len(jlist)-1]/targetLib.units.voltage.magnitude/targetLib.units.energy.magnitude))
-                outline += tmp_line+"\" \\"
-            ##  add "," for else 
-            else:	
-                #outline += str(self.list2_eclk[i][len(jlist)-1])+"\", \\"
-                tmp_line = str("{:5f}".format(self.list2_eclk[i][len(jlist)-1]/targetLib.units.voltage.magnitude/targetLib.units.energy.magnitude))
-                outline += tmp_line+"\", \\"
-            self.lut_eclk.append(outline)
-        self.lut_eclk.append(");")
-
-    ## clock input capacitance 
-    def set_list2_cclk(self, list2_cclk=[]):
-        self.list2_cclk = list2_cclk 
-
-    def print_list2_cclk(self, ilist, jlist):
-        for i in range(len(ilist)):
-            for j in range(len(jlist)):
-                print(self.list2_cclk[i][j])
-    
-    def print_lut_cclk(self):
-        for i in range(len(self.lut_cclk)):
-            print(self.lut_cclk[i])
-
-    def average_list2_cclk(self, targetLib, ilist, jlist):
-        ## output average of input capacitance
-        ## (do not write table)
-        self.lut_cclk = 0
-        for i in range(len(ilist)):
-            for j in range(len(jlist)):
-                self.lut_cclk += self.list2_cclk[i][j]
-        #self.cclk = str(self.lut_cclk / (len(ilist) * len(jlist))/targetLib.units.capacitance.magnitude) ## use average
-        self.cclk = str(self.lut_cclk / (len(ilist) * len(jlist))) ## use average
-        #print("store cclk:"+str(self.cclk))
-
-    ## leak power
-    def set_list2_pleak(self, list2_pleak=[]):
-        self.list2_pleak = list2_pleak 
-
-    def print_list2_pleak(self, ilist, jlist):
-        for i in range(len(ilist)):
-            for j in range(len(jlist)):
-                print(self.list2_pleak[i][j])
-    
-    def print_lut_pleak(self):
-        for i in range(len(self.lut_pleak)):
-            print(self.lut_pleak[i])
-
-    def write_list2_pleak(self, targetLib, ilist, jlist):
-        ## output average of leak power
-        ## (do not write table)
-        self.lut_pleak = 0
-        for i in range(len(ilist)):
-            for j in range(len(jlist)):
-                self.lut_pleak += self.list2_pleak[i][j]
-        self.pleak = str(self.lut_pleak / (len(ilist) * len(jlist))/targetLib.units.leakage_power.magnitude) # use average
-    
-    ## setup (for flop)
-    def set_list2_setup(self, list2_setup=[]):
-        self.list2_setup = list2_setup 
-
-    def print_list2_setup(self, ilist, jlist):
-        for i in range(len(ilist)):
-            for j in range(len(jlist)):
-                print(self.list2_setup[i][j])
-    
-    def print_lut_setup(self):
-        for i in range(len(self.lut_setup)):
-            print(self.lut_setup[i])
-
-    def write_list2_setup(self, targetLib, ilist, jlist):
-        ## index_1
-        outline = "index_1(\""
-        self.lut_setup = []
-        for j in range(len(jlist)-1):
-            outline += str(jlist[j])+", " 
-        outline += str(jlist[len(jlist)-1])+"\");" 
-        #print(outline)
-        self.lut_setup.append(outline)
-        ## index_2
-        outline = "index_2(\""
-        for i in range(len(ilist)-1):
-            outline += str(ilist[i])+", " 
-        outline += str(ilist[len(ilist)-1])+"\");" 
-        self.lut_setup.append(outline)
-        ## values
-        self.lut_setup.append("values ( \\")
-        for i in range(len(ilist)):
-            outline = "\""
-            for j in range(len(jlist)-1):
-                #outline += str(self.list2_setup[i][j])+", "
-                print(str(i)+" "+str(j))
-                print(self.list2_setup)
-                print(str("{:5f}".format(self.list2_setup[i][j]/targetLib.units.time.magnitude)))
-                tmp_line = str("{:5f}".format(self.list2_setup[i][j]/targetLib.units.time.magnitude))
-                outline += tmp_line+", "
-            ## do not add "," for last line
-            if(i == (len(ilist)-1)): 
-                #outline += str(self.list2_setup[i][len(jlist)-1])+"\" \\"
-                tmp_line = str("{:5f}".format(self.list2_setup[i][len(jlist)-1]/targetLib.units.time.magnitude))
-                outline += tmp_line+"\" \\"
-            ##  add "," for else 
-            else:	
-                #outline += str(self.list2_setup[i][len(jlist)-1])+"\", \\"
-                tmp_line = str("{:5f}".format(self.list2_setup[i][len(jlist)-1]/targetLib.units.time.magnitude))
-                outline += tmp_line+"\", \\"
-            self.lut_setup.append(outline)
-        self.lut_setup.append(");")
-
-    ## hold (for flop)
-    def set_list2_hold(self, list2_hold=[]):
-        self.list2_hold = list2_hold 
-
-    def print_list2_hold(self, ilist, jlist):
-        for i in range(len(ilist)):
-            for j in range(len(jlist)):
-                print(self.list2_hold[i][j])
-    
-    def print_lut_hold(self):
-        for i in range(len(self.lut_hold)):
-            print(self.lut_hold[i])
-
-    def write_list2_hold(self, targetLib, ilist, jlist):
-        ## index_1
-        outline = "index_1(\""
-        self.lut_hold = []
-        for j in range(len(jlist)-1):
-            outline += str(jlist[j])+", " 
-        outline += str(jlist[len(jlist)-1])+"\");" 
-        #print(outline)
-        self.lut_hold.append(outline)
-        ## index_2
-        outline = "index_2(\""
-        for i in range(len(ilist)-1):
-            outline += str(ilist[i])+", " 
-        outline += str(ilist[len(ilist)-1])+"\");" 
-        self.lut_hold.append(outline)
-        ## values
-        self.lut_hold.append("values ( \\")
-        for i in range(len(ilist)):
-            outline = "\""
-            for j in range(len(jlist)-1):
-                #outline += str(self.list2_hold[i][j])+", "
-                tmp_line = str("{:5f}".format(self.list2_hold[i][j]/targetLib.units.time.magnitude))
-                outline += tmp_line+", "
-            ## do not add "," for last line
-            if(i == (len(ilist)-1)): 
-                #outline += str(self.list2_hold[i][len(jlist)-1])+"\" \\"
-                tmp_line = str("{:5f}".format(self.list2_hold[i][len(jlist)-1]/targetLib.units.time.magnitude))
-                outline += tmp_line+"\" \\"
-            ##  add "," for else 
-            else:	
-                #outline += str(self.list2_hold[i][len(jlist)-1])+"\", \\"
-                tmp_line = str("{:5f}".format(self.list2_hold[i][len(jlist)-1]/targetLib.units.time.magnitude))
-                outline += tmp_line+"\", \\"
-            self.lut_hold.append(outline)
-        self.lut_hold.append(");")
-
diff --git a/characterizer/LogicCell.py b/characterizer/LogicCell.py
deleted file mode 100644
index e3e1b84..0000000
--- a/characterizer/LogicCell.py
+++ /dev/null
@@ -1,413 +0,0 @@
-import re
-from pathlib import Path
-
-from characterizer.HarnessSettings import HarnessSettings
-
-RECOGNIZED_LOGIC = [
-    'INV',
-    'BUF',
-    'AND2',  'AND3',  'AND4',
-    'OR2',   'OR3',   'OR4',
-    'NAND2', 'NAND3', 'NAND4',
-    'NOR2',  'NOR3',  'NOR4',
-    'AO21',  'AO22',
-    'OA21',  'OA22',
-    'AOI21', 'AOI22',
-    'OAI21', 'OAI22',
-    'XOR2',
-    'XNOR2',
-    'SEL2',
-    #'HA',
-    #'FA',
-    'DFF_PCPU',
-    'DFF_PCNU',
-    'DFF_NCPU',
-    'DFF_NCNU',
-    'DFF_PCPU_NR',
-    'DFF_PCPU_NRNS',
-]
- 
-class LogicCell:
-    def __init__ (self, name: str, logic: str, in_ports: list, out_ports: list, function: str, area: float = 0):
-        self.name = name            # cell name
-        self.logic = logic          # logic implemented by this cell
-        self.in_ports = in_ports    # input pin names
-        self.out_ports = out_ports  # output pin names
-        self.functions = function   # cell function
-
-        # Documentation
-        self.area = area        # cell area
-
-        # Characterization settings
-        self._netlist = None    # cell netlist
-        self._definition = None # cell definition (from netlist)
-        self._instance = None   # TODO: figure out what this represents, and briefly document here
-        self._in_slopes = []     # input pin slope
-        self.load = []          # output pin load
-        self.sim_timestep = 0   # simulation timestep
-
-        # From characterization results
-        self.harnesses = []     # list of harnessSettings
-        self.cins = []          # input pin capacitances
-        self.leakage_power = [] # cell leakage power
-
-        # Behavioral settings
-        self._is_exported = False   # whether the cell has been exported
-
-    def __repr__(self):
-        # TODO
-        return self.name
-
-    @property
-    def name(self) -> str:
-        return self._name
-
-    @name.setter
-    def name(self, value: str):
-        if value is not None and len(value) > 0:
-            self._name = str(value)
-        else:
-            raise ValueError(f'Invalid value for cell name: {value}')
-
-    @property
-    def logic(self) -> str:
-        return self._logic
-    
-    @logic.setter
-    def logic(self, value: str):
-        if value is not None:
-            if value in RECOGNIZED_LOGIC:
-                self._logic = str(value)
-            else:
-                raise ValueError(f'Unrecognized logic: {value}')
-        else:
-            raise ValueError(f'Invalid value for cell logic: {value}')
-
-    @property
-    def in_ports(self) -> list:
-        return self._in_ports
-
-    @in_ports.setter
-    def in_ports(self, value):
-        if value is not None:
-            if isinstance(value, str):
-                # Should be in the format "A B C"
-                # TODO: add parsing for comma separated as well
-                self._in_ports = value.split()
-            elif isinstance(value, list):
-                self._in_ports = value
-            else:
-                raise TypeError(f'Invalid type for in_ports: {type(value)}')
-        else:
-            raise ValueError(f'Invalid value for in_ports: {value}')
-
-    @property
-    def out_ports(self) -> list:
-        return self._out_ports
-
-    @out_ports.setter
-    def out_ports(self, value):
-        if value is not None:
-            if isinstance(value, str):
-                # Should be in the format "Y Z"
-                # TODO: add parsing for comma separated as well
-                self._out_ports = value.split()
-            elif isinstance(value, list):
-                self._out_ports = value
-            else:
-                raise TypeError(f'Invalid type for out_ports: {type(value)}')
-        else:
-            raise ValueError(f'Invalid value for out_ports: {value}')
-
-    @property
-    def functions(self) -> list:
-        return self._functions
-
-    @functions.setter
-    def functions(self, value):
-        if value is not None:
-            if isinstance(value, list):
-                self._functions = value
-            elif isinstance(value, str) and '=' in value:
-                # Should be in the format "Y=stuff"
-                self._functions = value.split('=')[1:] # Discard lefthand side of equation
-            else:
-                raise TypeError(f'Invalid type for cell functions: {type(value)}')
-        else:
-            raise ValueError(f'Invalid value for cell functions: {value}')
-
-    @property
-    def area(self) -> float:
-        return self._area
-
-    @area.setter
-    def area(self, value: float):
-        if value is not None:
-            self._area = float(value)
-        else:
-            raise ValueError(f'Invalid value for cell area: {value}')
-
-    @property
-    def netlist(self) -> str:
-        return self._netlist
-
-    @netlist.setter
-    def netlist(self, value):
-        if value is not None:
-            if isinstance(value, Path):
-                if not value.is_file():
-                    raise ValueError(f'Invalid value for netlist: {value} is not a file')
-                self._netlist = value
-            elif isinstance(value, str):
-                if not Path(value).is_file():
-                    raise ValueError(f'Invalid value for netlist: {value} is not a file')
-                self._netlist = Path(value)
-            else:
-                raise TypeError(f'Invalid type for netlist: {type(value)}')
-            # netlist is now set - update definition
-            with open(self.netlist, 'r') as netfile:
-                for line in netfile:
-                    if self.name.lower() in line.lower() and '.subckt' in line.lower():
-                        self.definition = line
-                netfile.close()
-            if self.definition is None:
-                raise ValueError(f'No cell definition found in netlist {value}')
-        else:
-            raise ValueError(f'Invalid value for netlist: {value}')
-
-    @property
-    def definition(self) -> str:
-        return self._definition
-
-    @definition.setter
-    def definition(self, value: str):
-        if value is not None:
-            if self.name.lower() not in value.lower():
-                raise ValueError(f'Cell name not found in cell definition: {value}')
-            elif '.subckt' not in value.lower():
-                raise ValueError(f'".subckt" not found in cell definition: {value}')
-            else:
-                self._definition = value
-            # definition is now set - update instance
-            circuit_call = re.sub('\$.*$', '', value).split()[1:]   # Delete .subckt
-            circuit_call.append(circuit_call.pop(0))                # Move circuit name to last element
-            circuit_call.insert(0,'XDUT')                           # Insert instance name
-            self.instance = ' '.join(circuit_call)
-        else:
-            raise ValueError(f'Invalid value for cell definiton: {value}')
-
-    @property
-    def instance(self) -> str:
-        return self._instance
-
-    @instance.setter
-    def instance(self, value: str):
-        if value is not None:
-            self._instance = value
-        else:
-            raise ValueError(f'Invalid value for instance: {value}')
-
-    @property
-    def in_slopes(self) -> list:
-        return self._in_slopes
-
-    def add_in_slope(self, value: float):
-        if value is not None:
-            self._in_slopes.append(float(value))
-        else:
-            raise ValueError(f'Invalid value for input pin slope: {value}')
-
-    @property
-    def is_exported(self) -> bool:
-        return self._is_exported
-
-    def set_exported(self):
-        self._is_exported = True
-
-    # TODO: def add_harness(self, port, )
-
-    def add_load(self, line="tmp"):
-        line = re.sub('\{','',line)
-        line = re.sub('\}','',line)
-        line = re.sub('^add_load ','',line)
-        tmp_array = line.split()
-        for w in tmp_array:
-            self.load.append(float(w))
-        #print (self.load)
-
-    def return_slope(self):
-        jlist = self.in_slopes
-        outline = "(\""
-        self.lut_prop = []
-        for j in range(len(jlist)-1):
-            outline += str(jlist[j])+", " 
-        outline += str(jlist[len(jlist)-1])+"\");" 
-        return outline
-
-    def return_load(self):
-        jlist = self.load
-        outline = "(\""
-        self.lut_prop = []
-        for j in range(len(jlist)-1):
-            outline += str(jlist[j])+", " 
-        outline += str(jlist[len(jlist)-1])+"\");" 
-        return outline
-
-    def add_model(self, line="tmp"):
-        tmp_array = line.split()
-        self.model = tmp_array[1] 
-
-    def add_simulation_timestep(self, line="tmp"):
-        tmp_array = line.split()
-        ## if auto, amd slope is defined, use 1/10 of min slope
-        if ((tmp_array[1] == 'auto') and (self.in_slopes[0] != None)):
-            self.sim_timestep = float(self.in_slopes[0])/10 
-            print ("auto set simulation timestep")
-        else:
-            self.sim_timestep = float(tmp_array[1])
-
-    def set_inport_cap_pleak(self, index, harness):
-        ## average leak power of all harness
-        self.leakage_power += harness.pleak 
-
-    def add_clock_slope(self, line="tmp"):
-        tmp_array = line.split()
-        ## if auto, amd slope is defined, use mininum slope
-        if (tmp_array[1] == 'auto'):
-            self.cslope = float(self.in_slopes[0]) 
-            print ("auto set clock slope as mininum slope.")
-        else:
-            self.cslope = float(tmp_array[1]) 
-
-    ## this defines lowest limit of setup edge
-    def add_simulation_setup_lowest(self, line="tmp"):
-        tmp_array = line.split()
-        ## if auto, amd slope is defined, use 10x of max slope 
-        ## "10" should be the same value of tstart1 and tclk5 in spice 
-        if ((tmp_array[1] == 'auto') and (self.in_slopes[-1] != None)):
-            self.sim_setup_lowest = float(self.in_slopes[-1]) * -10 
-            print ("auto set setup simulation time lowest limit")
-        else:
-            self.sim_setup_lowest = float(tmp_array[1]) 
-            
-    ## this defines highest limit of setup edge
-    def add_simulation_setup_highest(self, line="tmp"):
-        tmp_array = line.split()
-        ## if auto, amd slope is defined, use 10x of max slope 
-        if ((tmp_array[1] == 'auto') and (self.in_slopes[-1] != None)):
-            self.sim_setup_highest = float(self.in_slopes[-1]) * 10 
-            print ("auto set setup simulation time highest limit")
-        else:
-            self.sim_setup_highest = float(tmp_array[1])
-            
-    def add_simulation_setup_timestep(self, line="tmp"):
-        tmp_array = line.split()
-        ## if auto, amd slope is defined, use 1/10x min slope
-        if ((tmp_array[1] == 'auto') and (self.in_slopes[0] != None)):
-            self.sim_setup_timestep = float(self.in_slopes[0])/10
-            print ("auto set setup simulation timestep")
-        else:
-            self.sim_setup_timestep = float(tmp_array[1])
-            
-    ## this defines lowest limit of hold edge
-    def add_simulation_hold_lowest(self, line="tmp"):
-        tmp_array = line.split()
-        ## if auto, amd slope is defined, use very small val. 
-        #remove# if hold is less than zero, pwl time point does not be incremental
-        #remove# and simulation failed
-        if ((tmp_array[1] == 'auto') and (self.in_slopes[-1] != None)):
-            #self.sim_hold_lowest = float(self.in_slopes[-1]) * -5 
-            self.sim_hold_lowest = float(self.in_slopes[-1]) * -10 
-            #self.sim_hold_lowest = float(self.in_slopes[-1]) * 0.001 
-            print ("auto set hold simulation time lowest limit")
-        else:
-            self.sim_hold_lowest = float(tmp_array[1])
-            
-    ## this defines highest limit of hold edge
-    def add_simulation_hold_highest(self, line="tmp"):
-        tmp_array = line.split()
-        ## if auto, amd slope is defined, use 5x of max slope 
-        ## value should be smaller than "tmp_max_val_loop" in holdSearchFlop
-        if ((tmp_array[1] == 'auto') and (self.in_slopes[-1] != None)):
-            #self.sim_hold_highest = float(self.in_slopes[-1]) * 5 
-            self.sim_hold_highest = float(self.in_slopes[-1]) * 10 
-            print ("auto set hold simulation time highest limit")
-        else:
-            self.sim_hold_highest = float(tmp_array[1])
-            
-    def add_simulation_hold_timestep(self, line="tmp"):
-        tmp_array = line.split()
-        ## if auto, amd slope is defined, use 1/10x min slope
-        if ((tmp_array[1] == 'auto') and (self.in_slopes[0] != None)):
-            self.sim_hold_timestep = float(self.in_slopes[0])/10 
-            print ("auto set hold simulation timestep")
-        else:
-            self.sim_hold_timestep = float(tmp_array[1])
-
-    ## calculate ave of cin for each inports
-    ## cin is measured two times and stored into 
-    ## neighborhood harness, so cin of (2n)th and 
-    ## (2n+1)th harness are averaged out
-    def set_cin_avg(self, targetLib, harnessList, port="data"):
-        tmp_cin = 0
-        tmp_index = 0
-        for targetHarness in harnessList:
-            if((port.lower() == 'clock')or(port.lower() == 'clk')):
-                tmp_cin += float(targetHarness.cclk)
-                ## if this is (2n+1) then store averaged 
-                ## cin into targetCell.cins
-                if((tmp_index % 2) == 1):
-                    self.cclks.append(str((tmp_cin / 2)/targetLib.units.capacitance.magnitude))
-                    tmp_cin = 0
-                tmp_index += 1
-                #print("stored cins:"+str(tmp_index)+" for clk")
-            elif((port.lower() == 'reset')or(port.lower() == 'rst')):
-                tmp_cin += float(targetHarness.cin) # .cin stores rst cap. 
-                ## if this is (2n+1) then store averaged 
-                ## cin into targetCell.cins
-                if((tmp_index % 2) == 1):
-                    self.crsts.append(str((tmp_cin / 2)/targetLib.units.capacitance.magnitude))
-                    tmp_cin = 0
-                tmp_index += 1
-                #print("stored cins:"+str(tmp_index)+" for rst")
-            elif(port.lower() == 'set'):
-                tmp_cin += float(targetHarness.cin) # .cin stores set cap.
-                ## if this is (2n+1) then store averaged 
-                ## cin into targetCell.cins
-                if((tmp_index % 2) == 1):
-                    self.csets.append(str((tmp_cin / 2)/targetLib.units.capacitance.magnitude))
-                    tmp_cin = 0
-                tmp_index += 1
-                #print("stored cins:"+str(tmp_index)+" for set")
-            else:	
-                tmp_cin += float(targetHarness.cin) # else, .cin stores inport cap.
-                ## if this is (2n+1) then store averaged 
-                ## cin into targetCell.cins
-                if((tmp_index % 2) == 1):
-                    self.cins.append(str((tmp_cin / 2)/targetLib.units.capacitance.magnitude))
-                    tmp_cin = 0
-                tmp_index += 1
-                #print("stored cins:"+str(tmp_index)+" for data")
-            #print("stored cins:"+str(tmp_index))
-
-class SequentialCell(LogicCell):
-    def __init__(self, name: str, logic: str, in_ports: list, out_ports: list, clock_pin: str, set_pin: str, reset_pin: str, flops: str, function: str, area: float = 0):
-        super().__init__(name, logic, in_ports, out_ports, function, area)
-        self.clock = clock_pin  # clock pin name
-        self.set = set_pin      # set pin name
-        self.reset = reset_pin  # reset pin name
-        self.flops = flops      # registers
-        self.cslope = 0         # input pin clock slope
-
-        # Characterization settings
-        self.sim_setup_lowest = 0   ## fastest simulation edge (pos. val.) 
-        self.sim_setup_highest = 0  ## lowest simulation edge (pos. val.) 
-        self.sim_setup_timestep = 0 ## timestep for setup search (pos. val.) 
-        self.sim_hold_lowest = 0    ## fastest simulation edge (pos. val.) 
-        self.sim_hold_highest = 0   ## lowest simulation edge (pos. val.) 
-        self.sim_hold_timestep = 0  ## timestep for hold search (pos. val.) 
-
-        # From characterization results
-        self.cclks = []     # clock pin capacitance
-        self.csets = []     # set pin capacitance
-        self.crsts = []     # reset pin capacitance
diff --git a/characterizer/LogicParser.py b/characterizer/LogicParser.py
new file mode 100644
index 0000000..d85ab29
--- /dev/null
+++ b/characterizer/LogicParser.py
@@ -0,0 +1,242 @@
+# Token definitions 
+T_NOT = 0
+T_GROUP = 1
+T_GROUP_END = 2
+T_AND = 3
+T_XOR = 4
+T_OR = 5
+T_OTHER = 6
+
+class Token:
+    def __init__(self, symbol: str) -> None:
+        self.symbol = symbol if not symbol == '!' else '~'
+        self.type = symbol
+
+    def __str__(self) -> str:
+        return self.symbol
+    
+    def __repr__(self) -> str:
+        return f'Token("{self.symbol}")'
+    
+    def __eq__(self, other) -> bool:
+        return isinstance(other, self.__class__) and self.symbol == other.symbol
+    
+    @property
+    def type(self) -> int:
+        return self._type
+
+    @type.setter
+    def type(self, value: str):
+        if value == '~' or value == '!':
+            self._type = T_NOT
+        elif value == '(':
+            self._type = T_GROUP
+        elif value == ')':
+            self._type = T_GROUP_END
+        elif value == '&':
+            self._type = T_AND
+        elif value == '^':
+            self._type = T_XOR
+        elif value == '|':
+            self._type = T_OR
+        else:
+            self._type = T_OTHER
+
+# Rules
+RULES = lambda S : [
+    [Token('~'), 'E', 'O'],             # 0: E -> ~EO
+    [Token('('), 'E', Token(')'), 'O'], # 1: E -> (E)O
+    [S, 'O'],                           # 2: E -> [A-z,0-9,_]*O
+    [Token('&'), 'E', 'O'],             # 3: O -> &EO
+    [Token('^'), 'E', 'O'],             # 4: O -> ^EO
+    [Token('|'), 'E', 'O'],             # 5: O -> |EO
+    [],                                 # 6: O -> null
+]
+
+def _get_rule(stack_token, input_token) -> int:
+    """Look up a rule by the current stack token and input token"""
+    # The left column is the current stack token and the top row is the
+    # current input token. The cell selected by these two tokens is the
+    # rule we will use to resolve the stack token. Empty cells should
+    # result in errors.
+    # |     | '~' | '(' | '&' | '^' | '|' | anything else |
+    # | --- | --- | --- | --- | --- | --- | ------------- |
+    # | 'E' |  0  |  1  |     |     |     |       2       |
+    # | 'O' |     |     |  3  |  4  |  5  |       6       |
+    rule = 999 # Default to invalid rule
+    if stack_token == 'E':
+        if input_token.type == T_NOT:
+            rule = 0
+        elif input_token.type == T_GROUP:
+            rule = 1
+        elif input_token.type == T_OTHER:
+            rule = 2
+    elif stack_token == 'O':
+        if input_token.type == T_AND:
+            rule = 3
+        elif input_token.type == T_XOR:
+            rule = 4
+        elif input_token.type == T_OR:
+            rule = 5
+        elif input_token.type == T_OTHER or input_token.type == T_GROUP_END:
+            rule = 6
+    return rule
+
+def _parse(tokens: list) -> list:
+    stack = ['O', 'E']
+    position = 0
+    rule_sequence = []
+    while stack:
+        stack_token = stack.pop()
+        if isinstance(stack_token, Token):
+            # Pop resolved terminals
+            input_token = tokens[position]
+            if stack_token == input_token:
+                position += 1
+            else:
+                raise ValueError(f'Failed to parse logic string "{"".join([str(t) for t in tokens])}". Parsing failed at token "{input_token.symbol}" (position {position})')
+        else:
+            # Resolve rules using lookup table
+            try:
+                input_token = tokens[position]
+            except IndexError:
+                input_token = Token('') # Empty token of type T_OTHER
+            rule = _get_rule(stack_token, input_token)
+            try:
+                stack.extend(reversed(RULES(input_token)[rule]))
+            except IndexError: # Error due to nonexistent rule
+                raise ValueError(f'Failed to parse logic string "{"".join([str(t) for t in tokens])}". Parsing failed at position {position}')
+            rule_sequence.append(rule)
+    
+    # Now that we know the production rules, produce an AST in prefix format
+    syntax_tree = []
+    named_tokens = [n for n in tokens if n.type == T_OTHER]
+    for rule in rule_sequence:
+        if rule == 0:
+            syntax_tree.extend(['O', '~'])
+        elif rule == 1:
+            syntax_tree.append('O')
+        elif rule == 2:
+            syntax_tree.extend(['O', named_tokens.pop(0).symbol])
+        elif rule == 3:
+            last_O_index = -1 - [t for t in reversed(syntax_tree)].index('O')
+            syntax_tree.insert(last_O_index+1, '&')
+        elif rule == 4:
+            last_O_index = -1 - [t for t in reversed(syntax_tree)].index('O')
+            syntax_tree.insert(last_O_index+1, '^')
+        elif rule == 5:
+            last_O_index = -1 - [t for t in reversed(syntax_tree)].index('O')
+            syntax_tree.insert(last_O_index+1, '|')
+        elif rule == 6:
+            try:
+                last_O_index = -1 - [t for t in reversed(syntax_tree)].index('O')
+                syntax_tree.pop(last_O_index)
+            except ValueError:
+                pass # We don't add O as aggressively in tree generation, so it's ok if it isn't present
+    return syntax_tree
+
+def _lex(expression: str) -> list:
+    """Convert a simple boolean logic expression into tokens"""
+    tokens = []
+    temp = ''
+    for c in ''.join(expression.split()):
+        if c in ['~', '!', '(', ')', '|', '&', '^']:
+            if temp:
+                tokens.append(Token(temp))
+                temp = ''
+            tokens.append(Token(c))
+        elif c.isalnum() or c == '_':
+            temp += c
+        else:
+            raise ValueError(f'Invalid character in boolean expression: {c}')
+    if temp:
+        tokens.append(Token(temp))
+    return tokens
+
+def parse_logic(expression: str) -> list:
+    """Parse a logic string and return the result in Polish prefix notation.
+    
+    This parser supports parentheses, NOT (~), AND (&), OR (|), and XOR (^) operations.
+    The return value is a list of tokens in Polish prefix notation.
+
+    Note that this parser does not currently support operator precedence. If you want
+    operations to happen in a particular order, make sure to use parentheses. 
+    """
+    return _parse(_lex(expression))
+
+def _resolve_unates(syntax_tree: list, target: str):
+    op = syntax_tree.pop(0)
+    if op == '~':
+        unate_l = -1
+        unate_r = None
+    elif op == '&':
+        unate_l = 1
+        unate_r = 1
+    elif op == '|' or op == '^':
+        # can't determine these yet - have to check which side contains the target
+        unate_l = 0
+        unate_r = 0
+    else:
+        return syntax_tree, {op: 1} # Return symbol
+    # Resolve left side
+    syntax_tree, unates = _resolve_unates(syntax_tree, target)
+    # Check for target in left side
+    if unate_l == 0 and target in unates:
+        unate_l = 1
+    else:
+        unate_l = -1
+    for k,u in unates.items():
+        unates[k] = unate_l * u
+    # Resolve right side
+    if unate_r is not None:
+        syntax_tree, unates_r = _resolve_unates(syntax_tree, target)
+        # Check for target in right side
+        if unate_r == 0 and target in unates_r:
+            unate_r = 1
+        else:
+            unate_r = -1
+        for k,u in unates_r.items():
+            unates[k] = unate_r * u
+    return syntax_tree, unates
+
+def generate_test_vectors(expression: str, inputs: list) -> list:
+    # This method doesn't work correctly for some expressions, and will be revisited and fixed later.
+    test_vectors = []
+    for target_port in inputs:
+        # Generate two test vectors for each input: one rising, one falling
+        for state in ['01', '10']:
+            input_vector = []
+            for port in inputs:
+                # Get unates for each variable
+                _, unates = _resolve_unates(parse_logic(expression), target_port)
+                # Make sure we don't have any unexpected ports
+                for k in unates.keys():
+                    if k not in inputs:
+                        raise ValueError(f'Unexpected port name {k} encountered during test vector generation')
+                # Use unates to determine port states
+                if port is target_port:
+                    input_vector.append(state)
+                    output_state = state if unates[port] > 0 else state[::-1]
+                else:
+                    input_vector.append(str(int(unates[port] > 0)))
+            test_vectors.append([output_state, input_vector])
+    return test_vectors
+
+
+if __name__ == '__main__':
+    # If run as main, test parser
+    assert parse_logic('~(A^B&C)') == ['~', '^', 'A', '&', 'B', 'C']
+    assert parse_logic('_^B | potato') == ['^', '_', '|', 'B', 'potato']
+    assert parse_logic('~~~~A') == ['~', '~', '~', '~', 'A']
+    assert parse_logic('(~(A&~C)) ^ B') == ['^', '~', '&', 'A', '~', 'C', 'B']
+    assert parse_logic('A&B&C&D&E&F&G&H&I&J&K') == ['&', 'A', '&', 'B', '&', 'C', '&', 'D', '&', 'E', '&', 'F', '&', 'G', '&', 'H', '&', 'I', '&', 'J', 'K']
+    tokens = _lex('~&^|')
+    assert tokens == [Token('~'), Token('&'), Token('^'), Token('|')]
+    try:
+        _parse(tokens) # We expect this to fail
+        raise AssertionError # This line should never be executed
+    except ValueError:
+        pass # Pass if we catch a ValueError
+    assert parse_logic('b&a&a') == ['&', 'b', '&', 'a', 'a']
+    assert parse_logic('(C&(A^B))|(A&B)') == ['|','&','C','^','A','B','&','A','B']
+    print(generate_test_vectors('~A', ['A']))
\ No newline at end of file
diff --git a/characterizer/README.md b/characterizer/README.md
new file mode 100644
index 0000000..572b673
--- /dev/null
+++ b/characterizer/README.md
@@ -0,0 +1,1 @@
+This folder contains python tools for characterizing standard cells (provided as spice sources) and generating liberty file contents from the characterization results.
\ No newline at end of file
diff --git a/characterizer/UnitsSettings.py b/characterizer/UnitsSettings.py
deleted file mode 100644
index 577a569..0000000
--- a/characterizer/UnitsSettings.py
+++ /dev/null
@@ -1,302 +0,0 @@
-class EngineeringUnit:
-    def __init__(self, symbol, magnitude = 1) -> None:
-        self.symbol = symbol
-        self.magnitude = magnitude
-
-    @property
-    def symbol(self):
-        return self._symbol
-
-    @symbol.setter
-    def symbol(self, value: str) -> None:
-        if value is not None and len(value) > 0:
-            self._symbol = value
-
-    @property
-    def magnitude(self):
-        return self._magnitude
-
-    @magnitude.setter
-    def magnitude(self, value) -> None:
-        if value is not None:
-            if isinstance(value, float) or value == 1:
-                # TODO: Add checks to make sure value is a multiple of 10^3n
-                self._magnitude = value
-            elif isinstance(value, str):
-                if value == 'Q':
-                    self.magnitude = 1e30
-                elif value == 'R':
-                    self.magnitude = 1e27
-                elif value == 'Y':
-                    self.magnitude = 1e24
-                elif value == 'Z':
-                    self.magnitude = 1e21
-                elif value == 'E':
-                    self.magnitude = 1e18
-                elif value == 'P':
-                    self.magnitude = 1e15
-                elif value == 'T':
-                    self.magnitude = 1e12
-                elif value == 'G':
-                    self.magnitude = 1e9
-                elif value == 'M':
-                    self.magnitude = 1e6
-                elif value == 'k':
-                    self.magnitude = 1e3
-                elif value == 'm':
-                    self.magnitude = 1e-3
-                elif value == 'μ':
-                    self.magnitude = 1e-6
-                elif value == 'n':
-                    self.magnitude = 1e-9
-                elif value == 'p':
-                    self.magnitude = 1e-12
-                elif value == 'f':
-                    self.magnitude = 1e-15
-                elif value == 'a':
-                    self.magnitude = 1e-18
-                elif value == 'z':
-                    self.magnitude = 1e-21
-                elif value == 'y':
-                    self.magnitude = 1e-24
-                elif value == 'r':
-                    self.magnitude = 1e-27
-                elif value == 'q':
-                    self.magnitude = 1e-30
-                else:
-                    raise ValueError("EngineeringUnit.magnitude accepts only SI units which correspond to values of 10^3N. See https://www.nist.gov/pml/owm/metric-si-prefixes")
-            else:
-                raise TypeError("EngineeringUnit.magnitude must be assigned as a float mulitple of 10 (such as 1e3) or as an SI prefix")
-        else:
-            raise TypeError("EngineeringUnit.magnitude must be assigned as an integer or SI prefix")
-                
-
-    def __repr__(self) -> str:
-        # Turn magnitude into a metric prefix
-        mag_repr = None
-        if self.magnitude == 1e30:
-            mag_repr = 'Q'
-        elif self.magnitude == 1e27:
-            mag_repr = 'R'
-        elif self.magnitude == 1e24:
-            mag_repr = 'Y'
-        elif self.magnitude == 1e21:
-            mag_repr = 'Z'
-        elif self.magnitude == 1e18:
-            mag_repr = 'E'
-        elif self.magnitude == 1e15:
-            mag_repr = 'P'
-        elif self.magnitude == 1e12:
-            mag_repr = 'T'
-        elif self.magnitude == 1e9:
-            mag_repr = 'G'
-        elif self.magnitude == 1e6:
-            mag_repr = 'M'
-        elif self.magnitude == 1e3:
-            mag_repr = 'k'
-        elif self.magnitude == 1e-3:
-            mag_repr = 'm'
-        elif self.magnitude == 1e-6:
-            mag_repr = 'μ'
-        elif self.magnitude == 1e-9:
-            mag_repr = 'n'
-        elif self.magnitude == 1e-12:
-            mag_repr = 'p'
-        elif self.magnitude == 1e-15:
-            mag_repr = 'f'
-        elif self.magnitude == 1e-18:
-            mag_repr = 'a'
-        elif self.magnitude == 1e-21:
-            mag_repr = 'z'
-        elif self.magnitude == 1e-24:
-            mag_repr = 'y'
-        elif self.magnitude == 1e-27:
-            mag_repr = 'r'
-        elif self.magnitude == 1e-30:
-            mag_repr = 'q'
-
-        if mag_repr is not None:
-            return f'{mag_repr}{self.symbol}'
-        elif self.magnitude == 1:
-            return self.symbol
-        else: # Handle the case where magnitude is not 1e3N
-            return f'{self.symbol} x {self.magnitude}'
-
-
-class UnitsSettings:
-    def __init__(self) -> None:
-        self._voltage = EngineeringUnit('V')
-        self._capacitance = EngineeringUnit('F', 1e-12)
-        self._resistance = EngineeringUnit('Ω')
-        self._current = EngineeringUnit('A', 1e-6)
-        self._time = EngineeringUnit('s', 1e-9)
-        self._leakage_power = EngineeringUnit('W', 1e-9)
-        self._energy = EngineeringUnit('J', 1e-12)
-
-    @property
-    def voltage(self):
-        return self._voltage
-
-    @voltage.setter
-    def voltage(self, value: str):
-        # Valid symbols are "V" or "Volts"
-        if value.lower().endswith('v'):
-            if value.lower() == 'v': # Case A: no SI prefix
-                self._voltage.symbol = 'V'
-                self._voltage.magnitude = 1
-            else: # Case B: SI prefix present
-                self._voltage.symbol = value[-1]
-                self._voltage.magnitude = value[:-1]
-        elif value.lower().endswith('volts'):
-            if value.lower() == 'volts': # Case A: no SI prefix
-                self._voltage.symbol = value
-                self._voltage.magnitude = 1
-            else: # Case B: SI prefix present
-                self._voltage.symbol = value[-5:]
-                self._voltage.magnitude = value[:-5]
-        else:
-            raise ValueError(f'Invalid voltage unit: {value}')
-
-    @property
-    def capacitance(self):
-        return self._capacitance
-
-    @capacitance.setter
-    def capacitance(self, value: str):
-        # Valid symbols are "F" or "Farads"
-        if value.lower().endswith('f'):
-            if value.lower() == 'f':
-                self._capacitance.symbol = 'F'
-                self._capacitance.magnitude = 1
-            else:
-                self._capacitance.symbol = value[-1]
-                self._capacitance.magnitude = value[:-1]
-        elif value.lower().endswith('farads'):
-            if value.lower() == 'farads':
-                self._capacitance.symbol = value
-                self._capacitance.magnitude = 1
-            else:
-                self._capacitance.symbol = value[-6:]
-                self._capacitance.magnitude = value[:-6]
-        else:
-            raise ValueError(f'Invalid capacitance unit: {value}')
-    
-    @property
-    def resistance(self):
-        return self._resistance
-
-    @resistance.setter
-    def resistance(self, value: str):
-        # Valid symbols are "Ω" or "Ohms"
-        if value.endswith('Ω'):
-            if value == 'Ω':
-                self._resistance.symbol = 'Ω'
-                self._resistance.magnitude = 1
-            else:
-                self._resistance.symbol = value[-1]
-                self._resistance.magnitude = value[:-1]
-        elif value.lower().endswith('ohms') or value.lower().endswith('ohm'):
-            if value.lower() == 'ohms' or value.lower() == 'ohm':
-                self._resistance.symbol = value
-                self._resistance.magnitude = 1
-            else:
-                self._resistance.symbol = value[-4:]
-                self._resistance.magnitude = value[:-4]
-        else:
-            raise ValueError(f'Invalid resistance unit: {value}')
-    
-    @property
-    def current(self):
-        return self._current
-
-    @current.setter
-    def current(self, value: str):
-        # Valid symbols are "A" or "Amps"
-        if value.lower().endswith('a'):
-            if value.lower() == 'a':
-                self._current.symbol = 'A'
-                self._current.magnitude = 1
-            else:
-                self._current.symbol = value[-1]
-                self._current.magnitude = value[:-1]
-        elif value.lower().endswith('amps'):
-            if value.lower() == 'amps':
-                self._current.symbol = value
-                self._current.magnitude = 1
-            else:
-                self._current.symbol = value[-4:]
-                self._current.magnitude = value[:-4]
-        else:
-            raise ValueError(f'Invalid current unit: {value}')
-    
-    @property
-    def time(self):
-        return self._time
-
-    @time.setter
-    def time(self, value: str):
-        # Valid symbols are "s" or "seconds"
-        if value.lower().endswith('seconds'):
-            if value.lower() == 'seconds':
-                self._time.symbol = value
-                self._time.magnitude = 1
-            else:
-                self._time.symbol = value[-7]
-                self._time.magnitude = value[:-7]
-        elif value.lower().endswith('s'):
-            if value.lower() == 's':
-                self._time.symbol = value
-                self._time.magnitude = 1
-            else:
-                self._time.symbol = value[-1]
-                self._time.magnitude = value[:-1]
-        else:
-            raise ValueError(f'Invalid time unit: {value}')
-    
-    @property
-    def leakage_power(self):
-        return self._leakage_power
-    
-    @leakage_power.setter
-    def leakage_power(self, value: str):
-        # Valid symbols are "W" or "Watts"
-        if value.lower().endswith('w'):
-            if value.lower() == 'w':
-                self._leakage_power.symbol = 'W'
-                self._leakage_power.magnitude = 1
-            else:
-                self._leakage_power.symbol = value[-1]
-                self._leakage_power.magnitude = value[:-1]
-        elif value.lower().endswith('watts'):
-            if value.lower() == 'watts':
-                self._leakage_power.symbol = value
-                self._leakage_power.magnitude = 1
-            else:
-                self._leakage_power.symbol = value[-5:]
-                self._leakage_power.magnitude = value[:-5]
-        else:
-            raise ValueError(f'Invalid leakage power unit: {value}')
-    
-    @property
-    def energy(self):
-        return self._energy
-    
-    @energy.setter
-    def energy(self, value: str):
-        # Valid symbols are "J" or "Joules"
-        if value.lower().endswith('j'):
-            if value.lower() == 'j':
-                self._energy.symbol = 'J'
-                self._energy.magnitude = 1
-            else:
-                self._energy.symbol = value[-1]
-                self._energy.magnitude = value[:-1]
-        elif value.lower().endswith('joules'):
-            if value.lower() == 'joules':
-                self._energy.symbol = value
-                self._energy.magnitude = 1
-            else:
-                self._energy.symbol = value[-6:]
-                self._energy.magnitude = value[:-6]
-        else:
-            raise ValueError(f'Invalid energy unit: {value}')
\ No newline at end of file
diff --git a/characterizer/char_comb.py b/characterizer/char_comb.py
index 1b55d8b..37159e4 100644
--- a/characterizer/char_comb.py
+++ b/characterizer/char_comb.py
@@ -1,761 +1,210 @@
-import re, subprocess, sys, threading 
+import re, subprocess
 
-from characterizer.HarnessSettings import HarnessSettings
-
-def runCombIn1Out1(targetLib, targetCell, expectationList, unate):
-    harnessList = []   # harness for each trial
-    harnessList2 = []  # list of harnessList
-
-    for trial in range(len(expectationList)):
-        print(f'Trial: {trial}')
-        tmp_Harness = HarnessSettings()
-        tmp_Harness.set_timing_type_comb()
-        tmp_Harness.set_timing_sense(unate)
-        tmp_inp0_val, tmp_outp0_val=expectationList[trial]
-        tmp_Harness.set_direction(tmp_outp0_val)
-        #print ("**"+targetCell.outports[0]+" "+targetCell.functions[0]+" "+ tmp_outp0_val)
-        tmp_Harness.set_target_outport (targetCell.out_ports[0], targetCell.functions[0], tmp_outp0_val)
-        ## case input0 is target input pin
-        if ((tmp_inp0_val == '01') or (tmp_inp0_val == '10')):
-            tmp_Harness.set_target_inport (targetCell.in_ports[0], tmp_inp0_val)
-            tmp_Harness.set_stable_inport ("NULL", "NULL")
-        else:
-            print ("Illegal input vector type!!")
-            print ("Check logic definition of this program!!")
-            
-        #tmp_Harness.set_leak_inportval ("1")
-        #tmp_Harness.set_nontarget_outport (targetCell.out_ports[0], "01")
-        spicef = "delay1_"+str(targetCell.name)+"_"+str(targetCell.in_ports[0])\
-            +str(tmp_inp0_val)+"_"+str(targetCell.out_ports[0])+str(tmp_outp0_val)
-        ## run spice and store result
-        if targetLib.mt_sim:
-            runSpiceCombDelayMultiThread(targetLib, targetCell, tmp_Harness, spicef)
-        else:
-            runSpiceCombDelay(targetLib, targetCell, tmp_Harness, spicef)
-        harnessList.append(tmp_Harness)
-        targetCell.harnesses.append(harnessList)
-
-    ## average cin of each harness
-    targetCell.set_cin_avg(targetLib, harnessList)
-
-#end runCombIn1Out1
-def runCombIn2Out1(targetLib, targetCell, expectationList2, unate):
-    harnessList = []
-    harnessList2 = []
-
-    for trial in range(len(expectationList2)):
-        tmp_Harness = HarnessSettings()
-        tmp_Harness.set_timing_type_comb()
-        tmp_Harness.set_timing_sense(unate)
-        tmp_inp0_val, tmp_inp1_val, tmp_outp0_val=expectationList2[trial]
-        tmp_Harness.set_direction(tmp_outp0_val)
-        tmp_Harness.set_target_outport (targetCell.out_ports[0], targetCell.functions[0], tmp_outp0_val)
-        # case input0 is target input pin
-        if ((tmp_inp0_val == '01') or (tmp_inp0_val == '10')):
-            tmp_Harness.set_target_inport (targetCell.in_ports[0], tmp_inp0_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[1], tmp_inp1_val)
-        # case input0 is target input pin
-        elif ((tmp_inp1_val == '01') or (tmp_inp1_val == '10')):
-            tmp_Harness.set_target_inport (targetCell.in_ports[1], tmp_inp1_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[0], tmp_inp0_val)
-        else:
-            print ("Illiegal input vector type!!")
-            print ("Check logic definition of this program!!")
-            
-        #tmp_Harness.set_leak_inportval ("1")
-        #tmp_Harness.set_nontarget_outport (targetCell.out_ports[0], "01")
-        spicef = "delay1_"+str(targetCell.name)+"_"+str(targetCell.in_ports[0])\
-            +str(tmp_inp0_val)+"_"+str(targetCell.in_ports[1])+str(tmp_inp1_val)\
-            +"_"+str(targetCell.out_ports[0])+str(tmp_outp0_val)
-        # run spice and store result
-        if targetLib.mt_sim:
-            runSpiceCombDelayMultiThread(targetLib, targetCell, tmp_Harness, spicef)
-        else:
-            runSpiceCombDelay(targetLib, targetCell, tmp_Harness, spicef)
-        harnessList.append(tmp_Harness)
-        harnessList2.append(harnessList)
-
-        # calculate avg of pleak
-        #if ((tmp_inp0_val == '01') or (tmp_inp0_val == '10')):
-        #	targetCell.set_inport_cap_pleak(0, tmp_Harness)
-        # case input0 is target input pin
-        #elif ((tmp_inp1_val == '01') or (tmp_inp1_val == '10')):
-        #	targetCell.set_inport_cap_pleak(1, tmp_Harness)
-
-    ## average cin of each harness
-    targetCell.set_cin_avg(targetLib, harnessList) 
-
-    return harnessList2
-#end runCombIn2Out1
-
-# TODO: def runCombIn2Out2
-
-def runCombIn3Out1(targetLib, targetCell, expectationList2, unate):
-    harnessList = []
-    harnessList2 = []
-
-    for trial in range(len(expectationList2)):
-        tmp_Harness = HarnessSettings()
-        tmp_Harness.set_timing_type_comb()
-        tmp_Harness.set_timing_sense(unate)
-        tmp_inp0_val, tmp_inp1_val, tmp_inp2_val, tmp_outp0_val=expectationList2[trial]
-        tmp_Harness.set_direction(tmp_outp0_val)
-        tmp_Harness.set_target_outport (targetCell.out_ports[0], targetCell.functions[0], tmp_outp0_val)
-        # case input0 is target input pin
-        if ((tmp_inp0_val == '01') or (tmp_inp0_val == '10')):
-            tmp_Harness.set_target_inport (targetCell.in_ports[0], tmp_inp0_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[1], tmp_inp1_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[2], tmp_inp2_val)
-        # case input1 is target input pin
-        elif ((tmp_inp1_val == '01') or (tmp_inp1_val == '10')):
-            tmp_Harness.set_target_inport (targetCell.in_ports[1], tmp_inp1_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[0], tmp_inp0_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[2], tmp_inp2_val)
-        # case input2 is target input pin
-        elif ((tmp_inp2_val == '01') or (tmp_inp2_val == '10')):
-            tmp_Harness.set_target_inport (targetCell.in_ports[2], tmp_inp2_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[0], tmp_inp0_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[1], tmp_inp1_val)
-        else:
-            print ("Illiegal input vector type!!")
-            print ("Check logic definition of this program!!")
-            
-        #tmp_Harness.set_leak_inportval ("1")
-        #tmp_Harness.set_nontarget_outport (targetCell.outports[0], "01")
-        spicef = "delay1_"+str(targetCell.name)+"_"\
-            +str(targetCell.in_ports[0])+str(tmp_inp0_val)\
-            +"_"+str(targetCell.in_ports[1])+str(tmp_inp1_val)\
-            +"_"+str(targetCell.in_ports[2])+str(tmp_inp2_val)\
-            +"_"+str(targetCell.out_ports[0])+str(tmp_outp0_val)
-        # run spice and store result
-        if targetLib.mt_sim:
-            runSpiceCombDelayMultiThread(targetLib, targetCell, tmp_Harness, spicef)
-        else:
-            runSpiceCombDelay(targetLib, targetCell, tmp_Harness, spicef)
-        harnessList.append(tmp_Harness)
-        harnessList2.append(harnessList)
-
-    ## average cin of each harness
-    targetCell.set_cin_avg(targetLib, harnessList) 
-
-    return harnessList2
-#end runCombIn3Out1
-
-# TODO: def runCombIn3Out2
-
-def runCombIn4Out1(targetLib, targetCell, expectationList2, unate):
-    harnessList = []
-    harnessList2 = []
-
-    for trial in range(len(expectationList2)):
-        tmp_Harness = HarnessSettings()
-        tmp_Harness.set_timing_type_comb()
-        tmp_Harness.set_timing_sense(unate)
-        tmp_inp0_val, tmp_inp1_val, tmp_inp2_val, tmp_inp3_val, tmp_outp0_val=expectationList2[trial]
-        tmp_Harness.set_direction(tmp_outp0_val)
-        tmp_Harness.set_target_outport (targetCell.out_ports[0], targetCell.functions[0], tmp_outp0_val)
-        # case input0 is target input pin
-        if ((tmp_inp0_val == '01') or (tmp_inp0_val == '10')):
-            tmp_Harness.set_target_inport (targetCell.in_ports[0], tmp_inp0_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[1], tmp_inp1_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[2], tmp_inp2_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[3], tmp_inp3_val)
-        # case input1 is target input pin
-        elif ((tmp_inp1_val == '01') or (tmp_inp1_val == '10')):
-            tmp_Harness.set_target_inport (targetCell.in_ports[1], tmp_inp1_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[0], tmp_inp0_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[2], tmp_inp2_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[3], tmp_inp3_val)
-        # case input2 is target input pin
-        elif ((tmp_inp2_val == '01') or (tmp_inp2_val == '10')):
-            tmp_Harness.set_target_inport (targetCell.in_ports[2], tmp_inp2_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[0], tmp_inp0_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[1], tmp_inp1_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[3], tmp_inp3_val)
-        elif ((tmp_inp3_val == '01') or (tmp_inp3_val == '10')):
-            tmp_Harness.set_target_inport (targetCell.in_ports[3], tmp_inp3_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[0], tmp_inp0_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[1], tmp_inp1_val)
-            tmp_Harness.set_stable_inport (targetCell.in_ports[2], tmp_inp2_val)
-        else:
-            print ("Illiegal input vector type!!")
-            print ("Check logic definition of this program!!")
-            
-        #tmp_Harness.set_leak_inportval ("1")
-        #tmp_Harness.set_nontarget_outport (targetCell.outports[0], "01")
-        spicef = "delay1_"+str(targetCell.name)+"_"\
-            +str(targetCell.in_ports[0])+str(tmp_inp0_val)\
-            +"_"+str(targetCell.in_ports[1])+str(tmp_inp1_val)\
-            +"_"+str(targetCell.in_ports[2])+str(tmp_inp2_val)\
-            +"_"+str(targetCell.in_ports[3])+str(tmp_inp3_val)\
-            +"_"+str(targetCell.out_ports[0])+str(tmp_outp0_val)
-        # run spice and store result
-        if targetLib.mt_sim:
-            runSpiceCombDelayMultiThread(targetLib, targetCell, tmp_Harness, spicef)
-        else:
-            runSpiceCombDelay(targetLib, targetCell, tmp_Harness, spicef)
-        harnessList.append(tmp_Harness)
-        harnessList2.append(harnessList)
-    
-    ## average cin of each harness
-    targetCell.set_cin_avg(targetLib, harnessList) 
-
-    return harnessList2
-#end  runCombIn4Out1
-
-def runSpiceCombDelayMultiThread(targetLib, targetCell, targetHarness, spicef):
-    list2_prop =   []
-    list2_tran =   []
-    list2_estart = []
-    list2_eend =   []
-    list2_eintl =   []
-    list2_ein =   []
-    list2_cin =   []
-    list2_pleak =   []
-    ## calculate whole slope length from logic threshold
-    tmp_slope_mag = 1 / (targetLib.logic_threshold_high - targetLib.logic_threshold_low)
-
-    thread_id = 0
-
-    results_prop_in_out  = dict()
-    results_trans_out    = dict()
-    results_energy_start = dict()
-    results_energy_end   = dict()
-    results_q_in_dyn     = dict()
-    results_q_out_dyn    = dict()
-    results_q_vdd_dyn    = dict()
-    results_q_vss_dyn    = dict()
-    results_i_in_leak    = dict()
-    results_i_vdd_leak   = dict()
-    results_i_vss_leak   = dict()
-    threadlist = list()
-    for tmp_slope in targetCell.in_slopes:
-        for tmp_load in targetCell.load:
-            thread = threading.Thread(target=runSpiceCombDelaySingle, \
-                                args=([targetLib, targetCell, targetHarness, spicef, \
-                                        tmp_slope, tmp_load, tmp_slope_mag, \
-                                        results_prop_in_out, results_trans_out,\
-                                        results_energy_start, results_energy_end,\
-                                        results_q_in_dyn, results_q_out_dyn, results_q_vdd_dyn, results_q_vss_dyn, \
-                                        results_i_in_leak, results_i_vdd_leak, results_i_vss_leak]),	\
-                                name="%d" % thread_id)
-            threadlist.append(thread)
-            thread_id += 1
-
-    for thread in threadlist:
-        thread.start() 
-
-    for thread in threadlist:
-        thread.join() 
-
-    thread_id = 0
-    for tmp_slope in targetCell.in_slopes:
-        tmp_list_prop =   []
-        tmp_list_tran =   []
-        tmp_list_estart = []
-        tmp_list_eend =   []
-        tmp_list_eintl =   []
-        tmp_list_ein =   []
-        tmp_list_cin =   []
-        tmp_list_pleak =   []
-        for tmp_load in targetCell.load:
-            #targetLib.print_msg(str(thread_id))
-            #targetLib.print_msg(str(results_prop_in_out))
-            #targetLib.print_msg(str(results_prop_in_out[str(thread_id)]))
-            tmp_list_prop.append(results_prop_in_out[str(thread_id)])
-            tmp_list_tran.append(results_trans_out[str(thread_id)])
-
-            ## intl. energy calculation
-            ## intl. energy is the sum of short-circuit energy and drain-diffusion charge/discharge energy
-            ## larger Ql: intl. Q, load Q 
-            ## smaller Qs: intl. Q
-            ## Eintl = QsV
-            if abs(results_q_vdd_dyn[str(thread_id)]) < abs(results_q_vss_dyn[str(thread_id)]):
-                res_q = results_q_vdd_dyn[str(thread_id)]
-            else:
-                res_q = results_q_vss_dyn[str(thread_id)]
-            tmp_list_eintl.append(abs(res_q*targetLib.vdd.voltage*targetLib.energy_meas_high_threshold \
-                - abs((results_energy_end[str(thread_id)] - results_energy_start[str(thread_id)])*(abs(results_i_vdd_leak[str(thread_id)]) \
-                + abs(results_i_vdd_leak[str(thread_id)]))/2*(targetLib.vdd.voltage*targetLib.energy_meas_high_threshold))))
-
-            ## input energy
-            tmp_list_ein.append(abs(results_q_in_dyn[str(thread_id)])*targetLib.vdd.voltage)
-
-            ## Cin = Qin / V
-            tmp_list_cin.append(abs(results_q_in_dyn[str(thread_id)])/(targetLib.vdd.voltage))
-
-            ## Pleak = average of Pleak_vdd and Pleak_vss
-            ## P = I * V
-            tmp_list_pleak.append((abs(results_i_vdd_leak[str(thread_id)])+abs(results_i_vdd_leak[str(thread_id)]))/2*(targetLib.vdd.voltage)) #
-            thread_id += 1
-
-        list2_prop.append(tmp_list_prop)
-        list2_tran.append(tmp_list_tran)
-        #list2_estart.append(tmp_list_estart)
-        #list2_eend.append(tmp_list_eend)
-        list2_eintl.append(tmp_list_eintl)
-        list2_ein.append(tmp_list_ein)
-        list2_cin.append(tmp_list_cin)
-        list2_pleak.append(tmp_list_pleak)
-
-
-    targetHarness.set_list2_prop(list2_prop)
-    #targetHarness.print_list2_prop(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_prop(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_prop()
-    targetHarness.set_list2_tran(list2_tran)
-    #targetHarness.print_list2_tran(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_tran(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_tran()
-    targetHarness.set_list2_eintl(list2_eintl)
-    #targetHarness.print_list2_eintl(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_eintl(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_eintl()
-    targetHarness.set_list2_ein(list2_ein)
-    #targetHarness.print_list2_ein(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_ein(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_ein()
-    targetHarness.set_list2_cin(list2_cin)
-    #targetHarness.print_list2_cin(targetCell.load, targetCell.in_slopes)
-    targetHarness.average_list2_cin(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_cin()
-    targetHarness.set_list2_pleak(list2_pleak)
-    #targetHarness.print_list2_pleak(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_pleak(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_pleak()
-
-def runSpiceCombDelaySingle(targetLib, targetCell, targetHarness, spicef, \
-                                        tmp_slope, tmp_load, tmp_slope_mag, \
-                                        results_prop_in_out, results_trans_out,\
-                                        results_energy_start, results_energy_end,\
-                                        results_q_in_dyn, results_q_out_dyn, results_q_vdd_dyn, results_q_vss_dyn, \
-                                        results_i_in_leak, results_i_vdd_leak, results_i_vss_leak):
-
-    targetLib.print_msg("start thread :"+str(threading.current_thread().name))
-
-    cap_line = ".param cap ="+str(tmp_load*targetLib.units.capacitance.magnitude)+"\n"
-    slew_line = ".param slew ="+str(tmp_slope*tmp_slope_mag*targetLib.units.time.magnitude)+"\n"
-    temp_line = ".temp "+str(targetLib.temperature)+"\n"
-    spicefo = str(spicef)+"_"+str(tmp_load)+"_"+str(tmp_slope)+".sp"
+def runCombinationalDelay(target_lib, target_cell, target_harness, spice_filename, in_slew, out_load):
+    spice_results_filename = str(spice_filename)+"_"+str(out_load)+"_"+str(in_slew)
 
     ## 1st trial, extract energy_start and energy_end
-    res_prop_in_out, res_trans_out, res_energy_start, res_energy_end, \
-        = genFileLogic_trial1(targetLib, targetCell, targetHarness, 0, cap_line, slew_line, temp_line, "none", "none", spicefo)
-
-    estart_line = ".param ENERGY_START = "+str(res_energy_start)+"\n"
-    eend_line = ".param ENERGY_END = "+str(res_energy_end)+"\n"
-
-    ## 2nd trial, extract energy
-    res_prop_in_out, res_trans_out, \
-        res_q_in_dyn, res_q_out_dyn, res_q_vdd_dyn, res_q_vss_dyn, \
-        res_i_in_leak, res_i_vdd_leak, res_i_vss_leak \
-        = genFileLogic_trial1(targetLib, targetCell, targetHarness, 1, cap_line, slew_line, temp_line, estart_line, eend_line, spicefo)
-    #targetLib.print_msg(str(res_prop_in_out)+" "+str(res_trans_out)+" "+str(res_energy_start)+" "+str(res_energy_end))
-    results_prop_in_out[threading.current_thread().name] = res_prop_in_out
-    results_trans_out[threading.current_thread().name]   = res_trans_out
-    results_energy_start[threading.current_thread().name]= res_energy_start
-    results_energy_end[threading.current_thread().name]  = res_energy_end
-    results_q_in_dyn[threading.current_thread().name]    = res_q_in_dyn
-    results_q_out_dyn[threading.current_thread().name]   = res_q_out_dyn
-    results_q_vdd_dyn[threading.current_thread().name]   = res_q_vdd_dyn
-    results_q_vss_dyn[threading.current_thread().name]   = res_q_vss_dyn
-    results_i_in_leak[threading.current_thread().name]   = res_i_in_leak
-    results_i_vdd_leak[threading.current_thread().name]  = res_i_vdd_leak
-    results_i_vss_leak[threading.current_thread().name]  = res_i_vss_leak
-
-    targetLib.print_msg("end thread :"+str(threading.current_thread().name))
-
-def runSpiceCombDelay(targetLib, targetCell, targetHarness, spicef):
-    list2_prop =   []
-    list2_tran =   []
-    list2_estart = []
-    list2_eend =   []
-    list2_eintl =   []
-    list2_ein =   []
-    list2_cin =   []
-    list2_pleak =   []
-    tmp_loop = 0
-    ## calculate whole slope length from logic threshold
-    tmp_slope_mag = 1 / (targetLib.logic_threshold_high - targetLib.logic_threshold_low)
-
-    for tmp_slope in targetCell.in_slopes:
-        tmp_list_prop =   []
-        tmp_list_tran =   []
-        tmp_list_estart = []
-        tmp_list_eend =   []
-        tmp_list_eintl =   []
-        tmp_list_ein =   []
-        tmp_list_cin =   []
-        tmp_list_pleak =   []
-        for tmp_load in targetCell.load:
-            tmp_loop += 1
-            cap_line = ".param cap ="+str(tmp_load*targetLib.units.capacitance.magnitude)+"\n"
-            slew_line = ".param slew ="+str(tmp_slope*tmp_slope_mag*targetLib.units.time.magnitude)+"\n"
-            temp_line = ".temp "+str(targetLib.temperature)+"\n"
-            spicefo = str(spicef)+"_"+str(tmp_load)+"_"+str(tmp_slope)+".sp"
-
-            ## 1st trial, extract energy_start and energy_end
-            res_prop_in_out, res_trans_out, res_energy_start, res_energy_end, \
-                = genFileLogic_trial1(targetLib, targetCell, targetHarness, 0, cap_line, slew_line, temp_line, "none", "none", spicefo)
-
-            estart_line = ".param ENERGY_START = "+str(res_energy_start)+"\n"
-            eend_line = ".param ENERGY_END = "+str(res_energy_end)+"\n"
-
-            ## 2nd trial, extract energy
-            res_prop_in_out, res_trans_out, \
-                res_q_in_dyn, res_q_out_dyn, res_q_vdd_dyn, res_q_vss_dyn, \
-                res_i_in_leak, res_i_vdd_leak, res_i_vss_leak \
-                = genFileLogic_trial1(targetLib, targetCell, targetHarness, 1, cap_line, slew_line, temp_line, estart_line, eend_line, spicefo)
-            #targetLib.print_msg(str(res_prop_in_out)+" "+str(res_trans_out)+" "+str(res_energy_start)+" "+str(res_energy_end))
-            tmp_list_prop.append(res_prop_in_out)
-            tmp_list_tran.append(res_trans_out)
-            #tmp_list_estart.append(res_energy_start)
-            #tmp_list_eend.append(res_energy_end)
-
-            ## intl. energy calculation
-            ## intl. energy is the sum of short-circuit energy and drain-diffusion charge/discharge energy
-            ## larger Ql: intl. Q, load Q 
-            ## smaller Qs: intl. Q
-            ## Eintl = QsV
-            if(abs(res_q_vdd_dyn) < abs(res_q_vss_dyn)):
-                tmp_list_eintl.append(abs(res_q_vdd_dyn*targetLib.vdd.voltage*targetLib.energy_meas_high_threshold-abs((res_energy_end - res_energy_start)*(abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2*(targetLib.vdd.voltage*targetLib.energy_meas_high_threshold))))
-                #targetLib.print_msg(str(abs(res_q_vdd_dyn*targetLib.vdd.voltage)))
-            else:
-                tmp_list_eintl.append(abs(res_q_vss_dyn*targetLib.vdd.voltage*targetLib.energy_meas_high_threshold-abs((res_energy_end - res_energy_start)*(abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2*(targetLib.vdd.voltage*targetLib.energy_meas_high_threshold))))
-                #targetLib.print_msg(str(abs(res_q_vss_dyn*targetLib.vdd.voltage)))
-
-            ## intl. energy calculation
-            ## Use VDD as intl. energy
-#			tmp_list_eintl.append(abs(res_q_vdd_dyn*targetLib.vdd.voltage)-abs((res_energy_end - res_energy_start)*(abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2*(targetLib.vdd.voltage)))
-#			targetLib.print_msg(str(abs(res_q_vdd_dyn*targetLib.vdd.voltage)))
-
-            ## input energy
-            tmp_list_ein.append(abs(res_q_in_dyn)*targetLib.vdd.voltage)
-
-            ## Cin = Qin / V
-            tmp_list_cin.append(abs(res_q_in_dyn)/(targetLib.vdd.voltage))
-
-            ## Pleak = average of Pleak_vdd and Pleak_vss
-            ## P = I * V
-            tmp_list_pleak.append((abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2*(targetLib.vdd.voltage)) #
-            #targetLib.print_msg("calculated pleak: "+str(float(abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2*targetLib.vdd.voltage*targetLib.units.voltage.magnitude)) #
-
-        list2_prop.append(tmp_list_prop)
-        list2_tran.append(tmp_list_tran)
-        #list2_estart.append(tmp_list_estart)
-        #list2_eend.append(tmp_list_eend)
-        list2_eintl.append(tmp_list_eintl)
-        list2_ein.append(tmp_list_ein)
-        list2_cin.append(tmp_list_cin)
-        list2_pleak.append(tmp_list_pleak)
-
-    #targetLib.print_msg(list2_prop)
-
-    targetHarness.set_list2_prop(list2_prop)
-    #targetHarness.print_list2_prop(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_prop(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_prop()
-    targetHarness.set_list2_tran(list2_tran)
-    #targetHarness.print_list2_tran(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_tran(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_tran()
-    targetHarness.set_list2_eintl(list2_eintl)
-    #targetHarness.print_list2_eintl(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_eintl(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_eintl()
-    targetHarness.set_list2_ein(list2_ein)
-    #targetHarness.print_list2_ein(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_ein(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_ein()
-    targetHarness.set_list2_cin(list2_cin)
-    #targetHarness.print_list2_cin(targetCell.load, targetCell.in_slopes)
-    targetHarness.average_list2_cin(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_cin()
-    targetHarness.set_list2_pleak(list2_pleak)
-    #targetHarness.print_list2_pleak(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_pleak(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_pleak()
-
-def genFileLogic_trial1(targetLib, targetCell, targetHarness, meas_energy, cap_line, slew_line, temp_line, estart_line, eend_line, spicef):
-    #print (spicef)
-    #print (estart_line)
-    #print (eend_line)
-    #print (spicef)
-    #print ("generate AND2\n")
-    #targetLib.print_msg(dir(targetLib))
-    with open(spicef,'w') as f:
-        outlines = []
-        outlines.append("*title: delay meas.\n")
-        outlines.append(".option brief nopage nomod post=1 ingold=2 autostop\n")
-        outlines.append(".inc '../"+targetCell.model+"'\n")
-        outlines.append(".inc '../"+str(targetCell.netlist)+"'\n")
-        outlines.append(temp_line)
-        outlines.append(".param _vdd = "+str(targetLib.vdd.voltage)+"\n")
-        outlines.append(".param _vss = "+str(targetLib.vss.voltage)+"\n")
-        outlines.append(".param _vnw = "+str(targetLib.nwell.voltage)+"\n")
-        outlines.append(".param _vpw = "+str(targetLib.pwell.voltage)+"\n")
-        outlines.append(".param cap = 10f \n")
-        outlines.append(".param slew = 100p \n")
-        outlines.append(".param _tslew = slew\n")
-        outlines.append(".param _tstart = slew\n")
-        outlines.append(".param _tend = '_tstart + _tslew'\n")
-        outlines.append(".param _tsimend = '_tslew * 10000' \n")
-        outlines.append(".param _Energy_meas_end_extent = "+str(targetLib.energy_meas_time_extent)+"\n")
+    trial_results = runCombinationalTrial(target_lib, target_cell, target_harness, in_slew, out_load, spice_results_filename)
+    energy_start = trial_results['energy_start']
+    energy_end = trial_results['energy_end']
+
+    ## 2nd trial
+    trial_results = runCombinationalTrial(target_lib, target_cell, target_harness, in_slew, out_load, spice_results_filename, energy_start, energy_end)
+    trial_results['energy_start'] = energy_start
+    trial_results['energy_end'] = energy_end
+
+    if not target_harness.results.get(str(in_slew)):
+        target_harness.results[str(in_slew)] = {}
+    target_harness.results[str(in_slew)][str(out_load)] = trial_results
+
+def runCombinationalTrial(target_lib, target_cell, target_harness, in_slew, out_load, output_filename: str, *energy):
+    print(f'Running {output_filename}')
+    outlines = [
+        f'*title: delay meas.\n',
+        f'.option brief nopage nomod post=1 ingold=2 autostop\n',
+        f".inc '../{target_cell.model}'\n",
+        f".inc '../{str(target_cell.netlist)}'\n",
+        f'.temp {str(target_lib.temperature)}\n',
+        f'.param _vdd = {str(target_lib.vdd.voltage)}\n',
+        f'.param _vss = {str(target_lib.vss.voltage)}\n',
+        f'.param _vnw = {str(target_lib.nwell.voltage)}\n',
+        f'.param _vpw = {str(target_lib.pwell.voltage)}\n',
+        f'.param cap = 10f\n', # TODO: is this correct?
+        f'.param slew = 100p \n', # TODO: is this correct?
+        f'.param _tslew = slew\n',
+        f'.param _tstart = slew\n',
+        f".param _tend = '_tstart + _tslew'\n",
+        f".param _tsimend = '_tslew * 10000'\n",
+        f'.param _Energy_meas_end_extent = {str(target_lib.energy_meas_time_extent)}\n\n',
+        f"VDD_DYN VDD_DYN 0 DC '_vdd'\n",
+        f"VSS_DYN VSS_DYN 0 DC '_vss'\n",
+        f"VNW_DYN VNW_DYN 0 DC '_vnw'\n",
+        f"VPW_DYN VPW_DYN 0 DC '_vpw'\n",
+        f'* output load calculation\n',
+        f'VOCAP VOUT WOUT DC 0\n',
+        f"VDD_LEAK VDD_LEAK 0 DC '_vdd'\n",
+        f"VSS_LEAK VSS_LEAK 0 DC '_vss'\n",
+        f"VNW_LEAK VNW_LEAK 0 DC '_vnw'\n",
+        f"VPW_LEAK VPW_LEAK 0 DC '_vpw'\n\n",
+    ]
+    ## in auto mode, simulation timestep is 1/10 of min. input slew
+    ## simulation runs 1000x of input slew time
+    outlines.append(f".tran {target_cell.sim_timestep}{str(target_lib.units.time)} '_tsimend' \n\n")
+
+    if target_harness.in_direction == 'rise':
+        outlines.append("VIN VIN 0 PWL(1p '_vss' '_tstart' '_vss' '_tend' '_vdd' '_tsimend' '_vdd') \n")
+    elif target_harness.in_direction == 'fall':
+        outlines.append("VIN VIN 0 PWL(1p '_vdd' '_tstart' '_vdd' '_tend' '_vss' '_tsimend' '_vss') \n")
+    outlines.append("VHIGH VHIGH 0 DC '_vdd' \n")
+    outlines.append("VLOW VLOW 0 DC '_vss' \n")
+
+    ##
+    ## delay measurement 
+    outlines.append("** Delay \n")
+    if target_harness.in_direction == 'rise':
+        prop_start_v = target_lib.logic_low_to_high_threshold_voltage()
+    else:
+        prop_start_v = target_lib.logic_high_to_low_threshold_voltage()
+    if target_harness.out_direction == 'rise':
+        prop_end_v = target_lib.logic_low_to_high_threshold_voltage()
+        trans_start_v = target_lib.logic_threshold_low_voltage()
+        trans_end_v = target_lib.logic_threshold_high_voltage()
+    else:
+        prop_end_v = target_lib.logic_low_to_high_threshold_voltage()
+        trans_start_v = target_lib.logic_threshold_high_voltage()
+        trans_end_v = target_lib.logic_threshold_low_voltage()
+    outlines.append("* Prop delay \n")
+    outlines.append(f".measure Tran PROP_IN_OUT trig v(VIN) val='{str(prop_start_v)}' {target_harness.in_direction}=1\n")
+    outlines.append(f"+ targ v(VOUT) val='{str(prop_end_v)}' {target_harness.out_direction}=1\n")
+    outlines.append("* Trans delay \n")
+    outlines.append(f".measure Tran TRANS_OUT trig v(VOUT) val='{str(trans_start_v)}' {target_harness.out_direction}=1\n")
+    outlines.append(f"+ targ v(VOUT) val='{str(trans_end_v)}' {target_harness.out_direction}=1\n")
+
+    # get ENERGY_START and ENERGY_END for energy calculation in 2nd round 
+    if not energy:
+        outlines.append("* For energy calculation \n")
+        outlines.append(f".measure Tran ENERGY_START when v(VIN)='{str(target_lib.energy_meas_low_threshold_voltage())}' {target_harness.in_direction}=1\n")
+        outlines.append(f".measure Tran ENERGY_END when v(VOUT)='{str(target_lib.energy_meas_high_threshold_voltage())}' {target_harness.out_direction}=1\n")
+
+    ## energy measurement 
+    elif energy:
+        outlines.append(f'.param ENERGY_START = {energy[0]}\n')
+        outlines.append(f'.param ENERGY_END = {energy[1]}\n')
+        outlines.append("* \n")
+        outlines.append("** In/Out Q, Capacitance \n")
+        outlines.append("* \n")
+        outlines.append(".measure Tran Q_IN_DYN integ i(VIN) from='ENERGY_START' to='ENERGY_END'  \n")
+        outlines.append(".measure Tran Q_OUT_DYN integ i(VOCAP) from='ENERGY_START' to='ENERGY_END*_Energy_meas_end_extent' \n")
         outlines.append(" \n")
-        outlines.append("VDD_DYN VDD_DYN 0 DC '_vdd' \n")
-        outlines.append("VSS_DYN VSS_DYN 0 DC '_vss' \n")
-        outlines.append("VNW_DYN VNW_DYN 0 DC '_vnw' \n")
-        outlines.append("VPW_DYN VPW_DYN 0 DC '_vpw' \n")
-        outlines.append("* output load calculation\n")
-        outlines.append("VOCAP VOUT WOUT DC 0\n")
-        #outlines.append("VDD_LEAK VDD_LEAK 0 DC '_vdd' \n")
-        #outlines.append("VSS_LEAK VSS_LEAK 0 DC '_vss' \n")
-        #outlines.append("VNW_LEAK VNW_LEAK 0 DC '_vnw' \n")
-        #outlines.append("VPW_LEAK VPW_LEAK 0 DC '_vpw' \n")
+        outlines.append("* \n")
+        outlines.append("** Energy \n")
+        outlines.append("*  (Total charge, Short-Circuit Charge) \n")
+        outlines.append(".measure Tran Q_VDD_DYN integ i(VDD_DYN) from='ENERGY_START' to='ENERGY_END*_Energy_meas_end_extent'  \n")
+        outlines.append(".measure Tran Q_VSS_DYN integ i(VSS_DYN) from='ENERGY_START' to='ENERGY_END*_Energy_meas_end_extent'  \n")
         outlines.append(" \n")
-        ## in auto mode, simulation timestep is 1/10 of min. input slew
-        ## simulation runs 1000x of input slew time
-        outlines.append(".tran "+str(targetCell.sim_timestep)+str(targetLib.units.time)+" '_tsimend'\n")
+        outlines.append("* Leakage current \n")
+        outlines.append(".measure Tran I_VDD_LEAK avg i(VDD_DYN) from='_tstart*0.1' to='_tstart'  \n")
+        outlines.append(".measure Tran I_VSS_LEAK avg i(VSS_DYN) from='_tstart*0.1' to='_tstart'  \n")
         outlines.append(" \n")
-
-        if(targetHarness.target_inport_val == "01"):
-            outlines.append("VIN VIN 0 PWL(1p '_vss' '_tstart' '_vss' '_tend' '_vdd' '_tsimend' '_vdd') \n")
-        elif(targetHarness.target_inport_val == "10"):
-            outlines.append("VIN VIN 0 PWL(1p '_vdd' '_tstart' '_vdd' '_tend' '_vss' '_tsimend' '_vss') \n")
-        elif(targetHarness.target_inport_val == "11"):
-            outlines.append("VIN VIN 0 DC '_vdd' \n")
-        elif(targetHarness.target_inport_val == "00"):
-            outlines.append("VIN VIN 0 DC '_vss' \n")
-        outlines.append("VHIGH VHIGH 0 DC '_vdd' \n")
-        outlines.append("VLOW VLOW 0 DC '_vss' \n")
-
-        ##
-        ## delay measurement 
-        outlines.append("** Delay \n")
-        outlines.append("* Prop delay \n")
-        if(targetHarness.target_inport_val == "01"):
-            outlines.append(".measure Tran PROP_IN_OUT trig v(VIN) val='"+str(targetLib.logic_low_to_high_threshold_voltage())+"' rise=1 \n")
-        elif(targetHarness.target_inport_val == "10"):
-            outlines.append(".measure Tran PROP_IN_OUT trig v(VIN) val='"+str(targetLib.logic_high_to_low_threshold_voltage())+"' fall=1 \n")
-        if(targetHarness.target_outport_val == "10"):
-            outlines.append("+ targ v(VOUT) val='"+str(targetLib.logic_high_to_low_threshold_voltage())+"' fall=1 \n")
-        elif(targetHarness.target_outport_val == "01"):
-            outlines.append("+ targ v(VOUT) val='"+str(targetLib.logic_low_to_high_threshold_voltage())+"' rise=1 \n")
-        outlines.append("* Trans delay \n")
-
-        if(targetHarness.target_outport_val == "10"):
-            outlines.append(".measure Tran TRANS_OUT trig v(VOUT) val='"+str(targetLib.logic_threshold_high_voltage())+"' fall=1\n")
-            outlines.append("+ targ v(VOUT) val='"+str(targetLib.logic_threshold_low_voltage())+"' fall=1 \n")
-        elif(targetHarness.target_outport_val == "01"):
-            outlines.append(".measure Tran TRANS_OUT trig v(VOUT) val='"+str(targetLib.logic_threshold_low_voltage())+"' rise=1\n")
-            outlines.append("+ targ v(VOUT) val='"+str(targetLib.logic_threshold_high_voltage())+"' rise=1 \n")
-
-        # get ENERGY_START and ENERGY_END for energy calculation in 2nd round 
-        if(meas_energy == 0):
-            outlines.append("* For energy calculation \n")
-            if(targetHarness.target_inport_val == "01"):
-                outlines.append(".measure Tran ENERGY_START when v(VIN)='"+str(targetLib.energy_meas_low_threshold_voltage())+"' rise=1 \n")
-            elif(targetHarness.target_inport_val == "10"):
-                outlines.append(".measure Tran ENERGY_START when v(VIN)='"+str(targetLib.energy_meas_high_threshold_voltage())+"' fall=1 \n")
-            if(targetHarness.target_outport_val == "01"):
-                outlines.append(".measure Tran ENERGY_END when v(VOUT)='"+str(targetLib.energy_meas_high_threshold_voltage())+"' rise=1 \n")
-            elif(targetHarness.target_outport_val == "10"):
-                outlines.append(".measure Tran ENERGY_END when v(VOUT)='"+str(targetLib.energy_meas_low_threshold_voltage())+"' fall=1 \n")
-
-        ##
-        ## energy measurement 
-        elif(meas_energy == 1):
-            outlines.append(estart_line)
-            outlines.append(eend_line)
-            outlines.append("* \n")
-            outlines.append("** In/Out Q, Capacitance \n")
-            outlines.append("* \n")
-            outlines.append(".measure Tran Q_IN_DYN integ i(VIN) from='ENERGY_START' to='ENERGY_END'  \n")
-            outlines.append(".measure Tran Q_OUT_DYN integ i(VOCAP) from='ENERGY_START' to='ENERGY_END*_Energy_meas_end_extent' \n")
-            outlines.append(" \n")
-            outlines.append("* \n")
-            outlines.append("** Energy \n")
-            outlines.append("*  (Total charge, Short-Circuit Charge) \n")
-            outlines.append(".measure Tran Q_VDD_DYN integ i(VDD_DYN) from='ENERGY_START' to='ENERGY_END*_Energy_meas_end_extent'  \n")
-            outlines.append(".measure Tran Q_VSS_DYN integ i(VSS_DYN) from='ENERGY_START' to='ENERGY_END*_Energy_meas_end_extent'  \n")
-            outlines.append(" \n")
-            outlines.append("* Leakage current \n")
-            outlines.append(".measure Tran I_VDD_LEAK avg i(VDD_DYN) from='_tstart*0.1' to='_tstart'  \n")
-            outlines.append(".measure Tran I_VSS_LEAK avg i(VSS_DYN) from='_tstart*0.1' to='_tstart'  \n")
-            outlines.append(" \n")
-            outlines.append("* Gate leak current \n")
-            outlines.append(".measure Tran I_IN_LEAK avg i(VIN) from='_tstart*0.1' to='_tstart'  \n")
-        else:
-            targetLib.print_msg("Error, meas_energy should 0 (disable) or 1 (enable)")
-            exit()
-
-        ## for ngspice batch mode 
-        outlines.append("*comment out .control for ngspice batch mode \n")
-        outlines.append("*.control \n")
-        outlines.append("*run \n")
-        outlines.append("*plot V(VIN) V(VOUT) \n")
-        outlines.append("*.endc \n")
-
-        outlines.append("XINV VIN VOUT VHIGH VLOW VDD_DYN VSS_DYN VNW_DYN VPW_DYN DUT \n")
-        outlines.append("C0 WOUT VSS_DYN 'cap'\n")
-        outlines.append(" \n")
-        outlines.append(".SUBCKT DUT IN OUT HIGH LOW VDD VSS VNW VPW \n")
-        # parse subckt definition
-        tmp_array = targetCell.instance.split()
-        tmp_line = tmp_array[0] # XDUT
-        #targetLib.print_msg(tmp_line)
-        for w1 in tmp_array:
-            # match tmp_array and harness 
-            # search target inport
-            is_matched = 0
-            w2 = targetHarness.target_inport
-            if(w1 == w2):
-                tmp_line += ' IN'
-                is_matched += 1
-            # search stable inport
-            for w2 in targetHarness.stable_inport:
-                if(w1 == w2):
-                    # this is stable inport
-                    # search index for this port
-                    index_val = targetHarness.stable_inport_val[targetHarness.stable_inport.index(w2)]
-                    if(index_val == '1'):
-                        tmp_line += ' HIGH'
-                        is_matched += 1
-                    elif(index_val == '0'):
-                        tmp_line += ' LOW'
-                        is_matched += 1
-                    else:
-                        targetLib.print_msg('Illigal input value for stable input')
-            # one target outport for one simulation
-            w2 = targetHarness.target_outport
-            #targetLib.print_msg(w1+" "+w2+"\n")
-            if(w1 == w2):
-                tmp_line += ' OUT'
-                is_matched += 1
-            # search non-terget outport
-            for w2 in targetHarness.nontarget_outport:
-                if(w1 == w2):
-                    # this is non-terget outport
-                    # search outdex for this port
-                    index_val = targetHarness.nontarget_outport_val[targetHarness.nontarget_outport.index(w2)]
-                    tmp_line += ' WFLOAT'+str(index_val)
+        outlines.append("* Gate leak current \n")
+        outlines.append(".measure Tran I_IN_LEAK avg i(VIN) from='_tstart*0.1' to='_tstart'  \n")
+
+    outlines.append("XINV VIN VOUT VHIGH VLOW VDD_DYN VSS_DYN VNW_DYN VPW_DYN DUT \n")
+    outlines.append("C0 WOUT VSS_DYN 'cap'\n")
+    outlines.append(" \n")
+    outlines.append(".SUBCKT DUT IN OUT HIGH LOW VDD VSS VNW VPW \n")
+
+    # parse subckt definition
+    port_list = target_cell.instance.split()
+    circuit_name = port_list.pop(-1)
+    tmp_line = port_list.pop(0)
+    for port in port_list:
+        # Check target inport
+        is_matched = 0
+        if port == target_harness.target_in_port:
+            tmp_line += ' IN'
+            is_matched += 1
+        # Check stable inport
+        for stable_port, state in zip(target_harness.stable_in_ports, target_harness.stable_in_port_states):
+            if port == stable_port:
+                if state == '1':
+                    tmp_line += ' HIGH'
                     is_matched += 1
-            if(w1.upper() == targetLib.vdd.name.upper()):
-                    tmp_line += ' '+w1.upper() 
+                elif state == '0':
+                    tmp_line += ' LOW'
                     is_matched += 1
-            if(w1.upper() == targetLib.vss.name.upper()):
-                    tmp_line += ' '+w1.upper() 
-                    is_matched += 1
-            if(w1.upper() == targetLib.pwell.name.upper()):
-                    tmp_line += ' '+w1.upper() 
-                    is_matched += 1
-            if(w1.upper() == targetLib.nwell.name.upper()):
-                    tmp_line += ' '+w1.upper() 
-                    is_matched += 1
-            ## show error if this port has not matched
-            if(is_matched == 0):
-                ## if w1 is wire name, abort
-                ## check this is instance tmp_array[0] or circuit name tmp_array[-1]
-                if((w1 != tmp_array[0]) and (w1 != tmp_array[-1])): 
-                    targetLib.print_error("port: "+str(w1)+" has not matched in netlist parse!!")
-                    
-        tmp_line += " "+str(tmp_array[len(tmp_array)-1])+"\n" # CIRCUIT NAME
-        outlines.append(tmp_line)
-        #targetLib.print_msg(tmp_line)
-
-        outlines.append(".ends \n")
-        outlines.append(" \n")
-        outlines.append(cap_line)
-        outlines.append(slew_line)
-                
-        outlines.append(".end \n") 
+                else:
+                    raise ValueError(f'Invalid state for port {port}: {state}')
+        # Check target outport
+        if port == target_harness.target_out_port:
+            tmp_line += ' OUT'
+            is_matched += 1
+        # Check non-target outports
+        for nontarget_port, state in zip(target_harness.nontarget_out_ports, target_harness.nontarget_out_port_states):
+            if port == nontarget_port:
+                tmp_line += f' WFLOAT{str(state)}'
+                is_matched += 1
+        # Check VDD, VSS, VNW, and VPW
+        if port.upper() == target_lib.vdd.name.upper():
+                tmp_line += f' {port.upper()}'
+                is_matched += 1
+        if port.upper() == target_lib.vss.name.upper():
+                tmp_line += f' {port.upper()}'
+                is_matched += 1
+        if port.upper() == target_lib.pwell.name.upper():
+                tmp_line += f' {port.upper()}'
+                is_matched += 1
+        if port.upper() == target_lib.nwell.name.upper():
+                tmp_line += f' {port.upper()}'
+                is_matched += 1
+        ## show error if this port has not matched
+        if not is_matched:
+            raise ValueError(f"Port {port} not found in netlist")
+    tmp_line += f" {circuit_name}\n"
+    outlines.append(tmp_line)
+    outlines.append(".ends \n")
+    outlines.append(" \n")
+    outlines.append(f".param cap ={out_load}{target_lib.units.capacitance.get_metric_prefix()}\n")
+    in_slew_mag = 1 / (target_lib.logic_threshold_high - target_lib.logic_threshold_low)
+    outlines.append(f".param slew ={in_slew*in_slew_mag}{target_lib.units.time.get_metric_prefix()}\n")
+    outlines.append(".end \n")
+    
+    with open(f'{output_filename}.sp', 'w') as f:
         f.writelines(outlines)
         f.close()
 
-    spicelis = spicef
-    spicelis += ".lis"
-    spicerun = spicef
-    spicerun += ".run"
+    if 'ngspice' in str(target_lib.simulator):
+        cmd = f'{str(target_lib.simulator.resolve())} -b {output_filename}.sp 1> {output_filename}.lis 2> /dev/null \n'
+    elif 'hspice' in str(target_lib.simulator):
+        cmd = f'{str(target_lib.simulator.resolve())} {output_filename}.sp -o {output_filename}.lis 2> /dev/null \n'
 
-    if 'ngspice' in str(targetLib.simulator):
-        cmd = f'{str(targetLib.simulator.resolve())} -b {str(spicef)} 1> {str(spicelis)} 2> /dev/null \n'
-    elif 'hspice' in str(targetLib.simulator):
-        cmd = f'{str(targetLib.simulator.resolve())} {str(spicef)} -o {str(spicelis)} 2> /dev/null \n'
-    with open(spicerun,'w') as f:
+    with open(f'{output_filename}.run', 'w') as f:
         outlines = []
-        outlines.append(cmd) 
+        outlines.append(cmd)
         f.writelines(outlines)
         f.close()
 
-    cmd = ['sh', spicerun]
-            
-    if targetLib.run_sim:
-        try:
-            subprocess.run(cmd)
-        except:
-            print ("Failed to launch spice")
-
-    # read results
-    with open(spicelis,'r') as f:
+    # run spice simulation
+    cmd = ['sh', f'{output_filename}.run']
+    if target_lib.run_sim:
+        subprocess.run(cmd)
+
+    # read results from lis file
+    results = {}
+    desired_measurements = ['prop_in_out', 'trans_out']
+    if energy:
+        desired_measurements += ['q_in_dyn', 'q_out_dyn', 'q_vdd_dyn', 'q_vss_dyn', 'i_vdd_leak', 'i_vss_leak', 'i_in_leak']
+    else:
+        desired_measurements += ['energy_start', 'energy_end']
+    with open(f'{output_filename}.lis', 'r') as f:
         for inline in f:
-            if 'hspice' in str(targetLib.simulator):
+            if any([f in inline for f in ['failed', 'Error']]):
+                raise NameError(f'An error occurred while running simulation. See {output_filename}.lis')
+            if 'hspice' in str(target_lib.simulator):
                 inline = re.sub('\=',' ',inline)
-            #targetLib.print_msg(inline)
-            # search measure
-            if 'failed' not in inline and 'Error' not in inline:
-                if 'prop_in_out' in inline:
-                    sparray = inline.split() # separate words with spaces
-                    res_prop_in_out = "{:e}".format(float(sparray[2].strip()))
-                elif 'trans_out' in inline:
-                    sparray = inline.split() # separate words with spaces
-                    res_trans_out = "{:e}".format(float(sparray[2].strip()))
-                if meas_energy == 0:
-                    if 'energy_start' in inline:
-                        sparray = inline.split() # separate words with spaces
-                        res_energy_start = "{:e}".format(float(sparray[2].strip()))
-                    elif 'energy_end' in inline:
-                        sparray = inline.split() # separate words with spaces
-                        res_energy_end = "{:e}".format(float(sparray[2].strip()))
-                if meas_energy == 1:
-                    if 'q_in_dyn' in inline:
-                        sparray = inline.split() # separate words with spaces
-                        res_q_in_dyn = "{:e}".format(float(sparray[2].strip()))
-                    elif 'q_out_dyn' in inline:
-                        sparray = inline.split() # separate words with spaces
-                        res_q_out_dyn = "{:e}".format(float(sparray[2].strip()))
-                    elif 'q_vdd_dyn' in inline:
-                        sparray = inline.split() # separate words with spaces
-                        res_q_vdd_dyn = "{:e}".format(float(sparray[2].strip()))
-                    elif 'q_vss_dyn' in inline:
-                        sparray = inline.split() # separate words with spaces
-                        res_q_vss_dyn = "{:e}".format(float(sparray[2].strip()))
-                    elif 'i_vdd_leak' in inline:
-                        sparray = inline.split() # separate words with spaces
-                        res_i_vdd_leak = "{:e}".format(float(sparray[2].strip()))
-                    elif 'i_vss_leak' in inline:
-                        sparray = inline.split() # separate words with spaces
-                        res_i_vss_leak = "{:e}".format(float(sparray[2].strip()))
-                    elif 'i_in_leak' in inline:
-                        sparray = inline.split() # separate words with spaces
-                        res_i_in_leak = "{:e}".format(float(sparray[2].strip()))
+            measurement = next((m for m in desired_measurements if m in inline), False)
+            if measurement:
+                results[measurement] = float(inline.split()[2])
         f.close()
-    #targetLib.print_msg(str(res_prop_in_out)+" "+str(res_trans_out)+" "+str(res_energy_start)+" "+str(res_energy_end))
-    # check spice finish successfully
-    try:
-        res_prop_in_out
-    except NameError:
-        targetLib.print_msg("Value res_prop_in_out is not defined!!")
-        targetLib.print_msg("Check simulation result in work directory")
-        sys.exit()
-    try:
-        res_trans_out
-    except NameError:
-        targetLib.print_msg("Value res_trans_out is not defined!!")
-        targetLib.print_msg("Check simulation result in work directory")
-        sys.exit()
-    if(meas_energy == 0):
-        return float(res_prop_in_out), float(res_trans_out), float(res_energy_start), float(res_energy_end)
-    elif(meas_energy == 1):
-        return float(res_prop_in_out), float(res_trans_out), \
-                float(res_q_in_dyn), float(res_q_out_dyn), float(res_q_vdd_dyn), float(res_q_vss_dyn), \
-                float(res_i_in_leak), float(res_i_vdd_leak), float(res_i_vss_leak)
+
+    return results
diff --git a/characterizer/char_seq.py b/characterizer/char_seq.py
index 561f5c2..24f4ede 100644
--- a/characterizer/char_seq.py
+++ b/characterizer/char_seq.py
@@ -1,1697 +1,464 @@
-import re, os, subprocess
-
+import re, subprocess
 import numpy as np
-from characterizer.HarnessSettings import HarnessSettings
-
-def runFlop(targetLib, targetCell, expectationList2):
-    harnessList = []   # harness for each trial
-    harnessList2 = []  # list of harnessList
-
-    D_val = None
-    CLK_val = None
-    SET_val = None
-    RST_val = None
-    Q_val = None
-
-    for trial in range(len(expectationList2)):
-        tmp_Harness = HarnessSettings() 
-        if(targetCell.logic == 'DFF_PCPU_NRNS'):
-            D_val, CLK_val, SET_val, RST_val, Q_val = expectationList2[trial]
-        elif(targetCell.logic == 'DFF_PCPU_NR'):
-            D_val, CLK_val, RST_val, Q_val = expectationList2[trial]
-        elif(targetCell.logic == 'DFF_PCPU'):
-            D_val, CLK_val, Q_val = expectationList2[trial]
-        else:
-            targetLib.print_error("Error! target cell "+str(targetCell.logic)+" is not defined!")
-
-        ## 
-        ## replace Q_val for recovery/removal
-        ##
-        if((SET_val == '01') or (SET_val == '10') or (RST_val == '01') or (RST_val == '10')):
-            if(Q_val == '01'):
-                Q_val = '10'
-            elif(Q_val == '10'):
-                Q_val = '01'
-            
-
-        tmp_Harness.set_target_inport (targetCell.in_ports[0], D_val)
-        tmp_Harness.set_target_outport (targetCell.out_ports[0], targetCell.functions[0], Q_val)
-        tmp_Harness.set_direction(Q_val)
-        tmp_Harness.set_target_clock (targetCell.clock, CLK_val)
-        #tmp_Harness.set_nontarget_outport (targetCell.outports[1])
-        #tmp_Harness.set_timing_flop_clock(CLK_val)
 
-
-        # activate RST and SET if defined
-        #targetLib.print_msg("RST:"+str(RST_val))
-        #targetLib.print_msg("SET:"+str(SET_val))
-        if((RST_val != None)and(SET_val != None)):
-            tmp_Harness.set_target_set (targetCell.set, SET_val)
-            tmp_Harness.set_target_reset (targetCell.reset, RST_val)
-            spicef = "c2q1_"+str(targetCell.name)+"_"\
-                    +str(targetCell.in_ports[0])+str(D_val)+"_"\
-                    +str(targetCell.clock)+str(CLK_val)+"_"\
-                    +str(targetCell.set)+str(SET_val)+"_"\
-                    +str(targetCell.reset)+str(RST_val)+"_"\
-                    +str(targetCell.out_ports[0])+str(Q_val)
-        elif(SET_val != None):
-            tmp_Harness.set_target_set (targetCell.set, SET_val)
-            spicef = "c2q1_"+str(targetCell.name)+"_"\
-                    +str(targetCell.in_ports[0])+str(D_val)+"_"\
-                    +str(targetCell.clock)+str(CLK_val)+"_"\
-                    +str(targetCell.set)+str(SET_val)+"_"\
-                    +str(targetCell.out_ports[0])+str(Q_val)
-        elif(RST_val != None):
-            tmp_Harness.set_target_reset (targetCell.reset, RST_val)
-            spicef = "c2q1_"+str(targetCell.name)+"_"\
-                    +str(targetCell.in_ports[0])+str(D_val)+"_"\
-                    +str(targetCell.clock)+str(CLK_val)+"_"\
-                    +str(targetCell.reset)+str(RST_val)+"_"\
-                    +str(targetCell.out_ports[0])+str(Q_val)
-        else:	
-            spicef = "c2q1_"+str(targetCell.name)+"_"\
-                    +str(targetCell.in_ports[0])+str(D_val)+"_"\
-                    +str(targetCell.clock)+str(CLK_val)+"_"\
-                    +str(targetCell.out_ports[0])+str(Q_val)
-
-        # select simulation type: clock(D2Q, D2C, C2Q, C2D), reset, set
-        # normal operation (clock edge)
-        if((D_val == '01') or (D_val == '10')):
-            tmp_Harness.set_timing_flop_inout(D_val, CLK_val, Q_val)
-            targetLib.print_msg_sim("D2Q simualtion mode!\n")
-            # run spice and store result
-            if targetLib.mt_sim:
-                runSpiceFlopDelayMT(targetLib, targetCell, tmp_Harness, spicef)
-            else:
-                runSpiceFlopDelay(targetLib, targetCell, tmp_Harness, spicef)
-        # reset operation (reset edge)
-        elif((RST_val == '01') or (RST_val == '10')):
-            tmp_Harness.set_timing_flop_reset(targetCell, RST_val, Q_val)
-            targetLib.print_msg_sim("R2Q simualtion mode!\n")
-            # run spice and store result
-            runSpiceFlopRecoveryRemoval(targetLib, targetCell, tmp_Harness, spicef)
-        # set operation (set edge)
-        elif((SET_val == '01') or (SET_val == '10')):
-            tmp_Harness.set_timing_flop_set(targetCell, SET_val, Q_val)
-            targetLib.print_msg_sim("S2Q simualtion mode!\n")
-            # run spice and store result
-            runSpiceFlopRecoveryRemoval(targetLib, targetCell, tmp_Harness, spicef)
+def runSequential(settings, cell, harness, output_filename):
+    spice_file = f'{output_filename}_rr'
+    tmp_time = 10 * max(cell.in_slews)
+    for slew in cell.in_slews:
+        for load in cell.out_loads:
+            # Sparse C-to-Q recovery search
+            if harness.target_in_port in [cell.set, cell.reset]:
+                harness.invert_set_reset() # invert set/reset for recovery/removal sim
+
+            # First stage: 10% output swing, imprecise search
+            mag = 20 * max(cell.in_slews)/min(cell.in_slews)
+            findSetupTime(settings, cell, harness, spice_file, load, slew,
+                          cell.sim_setup_lowest, 2 * cell.sim_setup_highest,
+                          cell.sim_setup_timestep * mag, tmp_time, 3)
+
+            # Middle stage: 100% output swing, precise search
+            while mag > 2:
+                setup_step = cell.sim_setup_timestep * mag
+                setup_min = harness.results[str(slew)][str(load)]['t_setup']
+                setup_max = setup_min + 2 * setup_step
+                findSetupTime(settings, cell, harness, spice_file, load, slew,
+                              setup_min, setup_max, setup_step, tmp_time, 3)
+                mag = mag / 2
+
+            # Final stage: full precision search
+            setup_min = harness.results[str(slew)][str(load)]['t_setup']
+            setup_max = setup_min + 2 * mag * cell.sim_setup_timestep
+            findSetupTime(settings, cell, harness, spice_file, load, slew,
+                          setup_min, setup_max, cell.sim_setup_timestep, tmp_time, 2)
+
+            # Removal search
+            if harness.target_in_port in [cell.set, cell.reset]:
+                harness.invert_set_reset() # restore set/reset for recovery/removal sim
+
+            # First stage: imprecise search
+            mag = 20 * max(cell.in_slews)/min(cell.in_slews)
+            findHoldTime(settings, cell, harness, spice_file, load, slew, tmp_time,
+                         4 * cell.sim_setup_highest, cell.sim_setup_lowest,
+                         cell.sim_hold_timestep * mag, 3)
+
+            # Middle stage: precise search
+            while mag > 2:
+                hold_step = cell.sim_hold_timestep * mag
+                hold_max = harness.results[str(slew)][str(load)]['t_hold'] + hold_step
+                hold_min = harness.results[str(slew)][str(load)]['t_hold'] - hold_step
+                findHoldTime(settings, cell, harness, spice_file, load, slew, tmp_time,
+                             hold_max, hold_min, hold_step, 3)
+                mag = mag / 2
+
+            #Final stage: full precision search
+            hold_max = harness.results[str(slew)][str(load)]['t_hold'] + 2 * mag * cell.sim_hold_timestep
+            hold_min = harness.results[str(slew)][str(load)]['t_hold'] - 2 * mag * cell.sim_hold_timestep
+            findHoldTime(settings, cell, harness, spice_file, load, slew, tmp_time,
+                         hold_max, hold_min, cell.sim_hold_timestep, 2)
+
+
+def findSetupTime(settings, cell, harness, output_filename, load, slew, setup_min, setup_max, setup_step, min_hold_time, timestep_scale):
+    # Scaling factors for simulation
+    tsimendmag_range = [1, 10]
+    tranmag_range = [1.1*settings.logic_threshold_low, 1]
+    prev_results = {}
+    # Run a trial for each setup time within the specified range
+    for t_setup in np.arange(setup_min-setup_step, setup_max+setup_step, setup_step):
+        first_stage_failed = False
+        for tsimendmag, tranmag in zip(tsimendmag_range, tranmag_range):
+            spice_file = f'{output_filename}_j{tranmag_range.index(tranmag)}_{load}_{slew}_setup{t_setup:,.4f}_hold{min_hold_time:,.4f}'
+
+            # Delay simulation
+            if not first_stage_failed:
+                try:
+                    harness.results[str(slew)][str(load)] = runSequentialTrial(
+                        settings, cell, harness, f'{spice_file}_1', load, slew,
+                        t_setup, min_hold_time, tsimendmag, tranmag, timestep_scale
+                    )
+                except NameError:
+                    first_stage_failed = True
+
+            # Energy simulation
+            if not first_stage_failed:
+                try:
+                    harness.results[str(slew)][str(load)] = runSequentialTrial(
+                        settings, cell, harness, f'{spice_file}_2', load, slew,
+                        t_setup, min_hold_time, tsimendmag, tranmag, timestep_scale,
+                        True # Use energy results from previous trial
+                    )
+                except NameError:
+                    first_stage_failed = True
+
+        # Add setup time to harness
+        harness.results[str(slew)][str(load)]['t_setup'] = t_setup
+
+        # If we failed this iteration, use previous results
+        if first_stage_failed or harness.results[str(slew)][str(load)]['prop_in_out'] > min(prev_results.get('prop_in_out'), 10*max(cell.in_slews)):
+            harness.results[str(slew)][str(load)] = prev_results
+            # TODO: Handle the possibility that we failed on iteration 1 and don't have a setup time
+            return
+
+        # Save previous results
+        prev_results = harness.results[str(slew)][str(load)]
+
+def findHoldTime(settings, cell, harness, output_filename, load, slew, setup_time, hold_max, hold_min, hold_step, timestep_scale):
+    # Scaling factors for simulation
+    tsimendmag_range = [1, 10]
+    tranmag_range = [1.1*settings.logic_threshold_low, 1]
+    prev_results = {}
+    # Run a trial for each hold time within the specified range
+    for t_hold in np.arange(hold_max+hold_step, hold_min-hold_step, -hold_step):
+        first_stage_failed = False
+        for tsimendmag, tranmag in zip(tsimendmag_range, tranmag_range):
+            spice_file = f'{output_filename}_j{tranmag_range.index(tranmag)}_{load}_{slew}_setup{setup_time:,.4f}_hold{t_hold}'
+
+            # Delay simulation
+            if not first_stage_failed:
+                try:
+                    harness.results[str(slew)][str(load)] = runSequentialTrial(
+                        settings, cell, harness, f'{spice_file}_1', load, slew,
+                        setup_time, t_hold, tsimendmag, tranmag, timestep_scale
+                    )
+                except NameError:
+                    first_stage_failed = True
+
+            # Energy simulation
+            if not first_stage_failed:
+                try:
+                    harness.results[str(slew)][str(load)] = runSequentialTrial(
+                        settings, cell, harness, f'{spice_file}_1', load, slew,
+                        setup_time, t_hold, tsimendmag, tranmag, timestep_scale,
+                        True # Use energy results from previous trial
+                    )
+                except NameError:
+                    first_stage_failed = True
+
+        # Add hold time to harness
+        harness.results[str(slew)][str(load)]['t_hold'] = t_hold
+
+        # If we failed this iteration, use previous results
+        if first_stage_failed or harness.results[str(slew)][str(load)]['prop_in_out'] >= 40*max(cell.in_slews):
+            harness.results[str(slew)][str(load)] = prev_results
+            # TODO: Handle the possibility that we failed on iteration 1 and don't have a hold time
+            return
+
+        # Save previous results
+        prev_results = harness.results[str(slew)][str(load)]
+
+def runSequentialTrial(settings, cell, harness, output_filename, load, slew, setup_time, hold_time, tsimendmag, tranmag, timestep_scale, *energy):
+    # Create a spice file to run the simulation
+    outlines = [
+        f'*title: flop delay meas.',
+        f'.option brief nopage nomod post=1 ingold=2 autostop',
+        f".inc '../{cell.model}'",
+        f".inc '../{str(cell.netlist)}'",
+        f'.temp {str(settings.temperature)}',
+        f'.param _vdd = {str(settings.vdd.voltage)}',
+        f'.param _vss = {str(settings.vss.voltage)}',
+        f'.param _vnw = {str(settings.nwell.voltage)}',
+        f'.param _vpw = {str(settings.pwell.voltage)}',
+        f'.param cap = 10f', # TODO: is this correct?
+        f'.param slew = 100p ', # TODO: is this correct?
+        f'.param cslew = 100p',
+        f'.param tunit = 100p',
+        f'.param tsetup = 100p',
+        f'.param thold = 100p',
+        f'.param tsimendmag = 100 tranmag = 1',
+        f'.param _tslew = slew',
+        f'.param _tclk1 = slew',                                # First clock posedge
+        f".param _tclk2 = '_tclk1 + cslew'",                    # 
+        f".param _tclk3 = '_tclk2 + tunit'",                    #
+        f".param _tclk4 = '_tclk3 + cslew'",                    # First clock negedge / Recovery
+        f".param _tclk5 = '_tclk4 + tunit'",                    # Removal
+        f".param _tstart1 = '_tclk5 + tunit * 10 + tsetup'",    # Data input start
+        f".param _tstart2 = '_tstart1 + slew'",                 # Varied with D edge
+        f".param _tend1 = '_tstart2 + tunit + thold'",          # Data input end
+        f".param _tend2 = '_tend1 + slew'",                     # Varied with D edge
+        f".param _tclk6 = '_tclk4 + tunit * 10'",               # Second clock posedge
+        f".param _tclk7 = '_tclk6 + cslew'",                    # Second clock negedge
+        f".param _tsimend = '_tend2 + tunit * 50 * tsimendmag'",
+        f'.param _Energy_meas_end_extent = {str(settings.energy_meas_time_extent)}\n',
+        f".measure Tran tstart2_to_tend1 param='_tend1 - _tstart2'",
+        f"VDD_DYN VDD_DYN 0 DC '_vdd'",
+        f"VSS_DYN VSS_DYN 0 DC '_vss'",
+        f"VNW_DYN VNW_DYN 0 DC '_vnw'",
+        f"VPW_DYN VPW_DYN 0 DC '_vpw'",
+        f"VDD_LEAK VDD_LEAK 0 DC '_vdd'",
+        f"VSS_LEAK VSS_LEAK 0 DC '_vss'",
+        f"VNW_LEAK VNW_LEAK 0 DC '_vnw'",
+        f"VPW_LEAK VPW_LEAK 0 DC '_vpw'",
+        f'* output load calculation',
+        f'VOCAP VOUT WOUT DC 0\n',
+        f".tran {cell.sim_timestep*timestep_scale}{str(settings.units.time)} '_tsimend'\n"
+    ]
+
+    # Data input: VIN
+    if harness.target_in_port in cell.in_ports:
+        # If the input port is targeted, vary with timing defined above
+        # TODO: Fix this so that we can actually handle multiple data inputs
+        target_vname = 'VIN'
+        if harness.in_direction == 'rise':
+            v0, v1 = "'_vss'", "'_vdd'" # Rise from vss to vdd
         else:
-            print("no suported input vector is inputted! error\n")
-            print("CLK: "+CLK_val+"\n")
-            print("D: "+D_val+"\n")
-            print("SET: "+SET_val+"\n")
-            print("RST: "+RST_val+"\n")
-            exit()
-        harnessList.append(tmp_Harness)
-        harnessList2.append(harnessList)
-
-        ## select cin in simulation: clock(D2Q, D2C, C2Q, C2D), reset, set
-        ## normal operation (clock edge)
-        if((D_val == '01') or (D_val == '10')):
-            ## set cin for clock
-            targetCell.set_cin_avg(targetLib, harnessList, "clk")
-            ## set cin for data
-            targetCell.set_cin_avg(targetLib, harnessList) 
-        ## reset operation (reset edge)
-        elif((RST_val == '01') or (RST_val == '10')):
-            targetCell.set_cin_avg(targetLib, harnessList, "rst")
-        ## set operation (set edge)
-        elif((SET_val == '01') or (SET_val == '10')):
-            targetCell.set_cin_avg(targetLib, harnessList, "set")
-        else:
-            targetLib.print_error("any input vector is inputted! error\n")
-
-    return harnessList2
-
-def runSpiceFlopDelay(targetLib, targetCell, targetHarness, spicef):
-    list2_prop =   []
-    list2_setup =   []
-    list2_hold =   []
-    list2_tran =   []
-    list2_estart = []
-    list2_eend =   []
-    list2_eintl =   []
-    list2_ein =   []
-    list2_cin =   []
-    list2_eclk =   []
-    list2_cclk =   []
-    list2_pleak =   []
-    for tmp_slope in targetCell.in_slopes:
-        res_list_prop =   []  # C2Q
-        res_list_setup =  []  # D2C(setup)
-        res_list_hold =   []  # C2D(hold)
-        res_list_tran =   []
-        res_list_estart = []
-        res_list_eend =   []
-        res_list_eintl =   []
-        res_list_ein =   []
-        res_list_cin =   []
-        res_list_eclk =   []
-        res_list_cclk =   []
-        res_list_pleak =   []
-
-        for tmp_load in targetCell.load:
-            tmp_max_val_loop = float(targetCell.in_slopes[-1]) * 10 # use x10 of max. slope for max val.
-            tmp_min_setup = tmp_max_val_loop # temporal value for setup 
-            tmp_tsetup1 = tmp_max_val_loop # temporal value for setup 
-            tmp_tsetup2 = tmp_max_val_loop # temporal value for setup 
-            tmp_tsetup3 = tmp_max_val_loop # temporal value for setup 
-            tmp_thold1 = tmp_max_val_loop # temporal value for setup 
-            tmp_thold2 = tmp_max_val_loop # temporal value for setup 
-            tmp_min_hold  = tmp_max_val_loop # temporal value for setup 
-
-            # C2Q and setup search (sparce)
-            # perform two-stage simulation
-            # 1st stage: sim w/  10-% output swing
-            # 2nd stage: sim w/ 100-% output swing
-            tsimendmag = [1, 10]; # magnify parameter of _tsimend
-            tranmag = [float(targetLib.logic_threshold_low)*1.1, 1];         # magnify parameter of transient simulation
-            tmp_min_prop_in_out   = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_min_prop_cin_out  = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_min_trans_out     = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_energy_start  = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_energy_end    = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_energy_clk_start  = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_energy_clk_end    = tmp_max_val_loop # temporal value for D2Qmin search
-
-            tmp_tstep_mag = 20
-            tmp_tstep_mag1 = float(targetCell.in_slopes[-1])/float(targetCell.in_slopes[0])* tmp_tstep_mag
-            targetLib.print_msg_sim("First stage sparse setup search, timestep: "\
-                +str(targetCell.sim_setup_timestep*tmp_tstep_mag1))
-            ( tmp_tsetup1, tmp_min_prop_in_out, _, _, _, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, _, _, _, _, _, _, _, _, _, _) \
-             = setupSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    targetCell.sim_setup_lowest, targetCell.sim_setup_highest, \
-                                                                    targetCell.sim_setup_timestep*tmp_tstep_mag1, tmp_min_hold, 5, spicef)
-
-            tmp_tstep_mag = 2
-            tmp_tstep_mag2 = tmp_tstep_mag1 
-            tmp_tstep_mag1 = tmp_tstep_mag1 / tmp_tstep_mag
-            targetLib.print_msg_sim("First stage sparse setup search, timestep: "\
-                +str(targetCell.sim_setup_timestep*tmp_tstep_mag1))
-            ( tmp_tsetup1, tmp_min_prop_in_out, _, _, _, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, _, _, _, _, _, _, _, _, _, _) \
-             = setupSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    tmp_tsetup1 - targetCell.sim_setup_timestep * tmp_tstep_mag2 *1,\
-                                                                    tmp_tsetup1 + targetCell.sim_setup_timestep * tmp_tstep_mag2 *2,\
-                                                                    targetCell.sim_setup_timestep * tmp_tstep_mag1, tmp_min_hold, 3, spicef)
-
-            while(tmp_tstep_mag1 > tmp_tstep_mag ):
-                tmp_tstep_mag2 = tmp_tstep_mag1 
-                tmp_tstep_mag1 = tmp_tstep_mag1 / tmp_tstep_mag
-                targetLib.print_msg_sim("Mid. stage precise setup search, timestep: "\
-                    +str(targetCell.sim_setup_timestep*tmp_tstep_mag1))
-                ( tmp_tsetup1, tmp_min_prop_in_out, _, _, _, tmp_min_trans_out, \
-                    tmp_energy_start, tmp_energy_end, _, _, _, _, _, _, _, _, _, _) \
-                 = setupSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                        tmp_tsetup1 - targetCell.sim_setup_timestep * tmp_tstep_mag2  *1,\
-                                                                        tmp_tsetup1 + targetCell.sim_setup_timestep * tmp_tstep_mag2  *3,\
-                                                                        targetCell.sim_setup_timestep * tmp_tstep_mag1, tmp_min_hold, 3, spicef)
-
-            targetLib.print_msg_sim("Last stage precise setup search, timestep: "\
-                +str(targetCell.sim_setup_timestep))
-            ( tmp_tsetup3, tmp_min_prop_in_out, _, _, _, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, _, _, _, _, _, _, _, _, _, _) \
-             = setupSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    tmp_tsetup1 - targetCell.sim_setup_timestep * tmp_tstep_mag2 *1,\
-                                                                    tmp_tsetup1 + targetCell.sim_setup_timestep * tmp_tstep_mag2 *3,\
-                                                                    targetCell.sim_setup_timestep, tmp_min_hold, 2, spicef)
-            
-            ## if target is D2Q, do standard setup/hold search
-            #if((targetHarness.target_inport_val == "01")or(targetHarness.target_inport_val == "10")):
-            tmp_tstep_mag = 20
-            tmp_tstep_mag1 = float(targetCell.in_slopes[-1])/float(targetCell.in_slopes[0])* tmp_tstep_mag
-            targetLib.print_msg_sim("First stage sparse hold search, timestep: "\
-                +str(targetCell.sim_hold_timestep*tmp_tstep_mag1))
-            ( tmp_thold1, tmp_min_prop_in_out,tmp_min_prop_cin_out, tmp_min_setup, tmp_min_hold, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                tmp_q_in_dyn, tmp_q_out_dyn, tmp_q_clk_dyn, tmp_q_vdd_dyn, tmp_q_vss_dyn, \
-                tmp_i_in_leak, tmp_i_vdd_leak, tmp_i_vss_leak) \
-            = holdSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    targetCell.sim_hold_lowest, targetCell.sim_hold_highest, \
-                                                                    targetCell.sim_hold_timestep*tmp_tstep_mag1, tmp_tsetup3, \
-                                                                    3, spicef)
-
-            tmp_tstep_mag = 2
-            tmp_tstep_mag2 = tmp_tstep_mag1
-            tmp_tstep_mag1 = tmp_tstep_mag1 /  tmp_tstep_mag
-            targetLib.print_msg_sim("First stage sparse hold search, timestep: "\
-                +str(targetCell.sim_setup_timestep*tmp_tstep_mag1))
-            ( tmp_thold1, tmp_min_prop_in_out,tmp_min_prop_cin_out, tmp_min_setup, tmp_min_hold, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                tmp_q_in_dyn, tmp_q_out_dyn, tmp_q_clk_dyn, tmp_q_vdd_dyn, tmp_q_vss_dyn, \
-                tmp_i_in_leak, tmp_i_vdd_leak, tmp_i_vss_leak) \
-             = holdSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    tmp_thold1 - targetCell.sim_hold_timestep * tmp_tstep_mag2 *2,\
-                                                                    tmp_thold1 + targetCell.sim_hold_timestep * tmp_tstep_mag2 *1,\
-                                                                    targetCell.sim_hold_timestep * tmp_tstep_mag1, tmp_tsetup3, 3, spicef)
-
-            while(tmp_tstep_mag1 > tmp_tstep_mag ):
-                tmp_tstep_mag2 = tmp_tstep_mag1
-                tmp_tstep_mag1 = tmp_tstep_mag1 /  tmp_tstep_mag
-                targetLib.print_msg_sim("Mid. stage precise hold search, timestep: "\
-                    +str(targetCell.sim_setup_timestep*tmp_tstep_mag1))
-                ( tmp_thold1, tmp_min_prop_in_out,tmp_min_prop_cin_out, tmp_min_setup, tmp_min_hold, tmp_min_trans_out, \
-                    tmp_energy_start, tmp_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                    tmp_q_in_dyn, tmp_q_out_dyn, tmp_q_clk_dyn, tmp_q_vdd_dyn, tmp_q_vss_dyn, \
-                    tmp_i_in_leak, tmp_i_vdd_leak, tmp_i_vss_leak) \
-                 = holdSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                        tmp_thold1 - targetCell.sim_hold_timestep * tmp_tstep_mag2 *3 ,\
-                                                                        tmp_thold1 + targetCell.sim_hold_timestep * tmp_tstep_mag2 *1 ,\
-                                                                        targetCell.sim_hold_timestep * tmp_tstep_mag1, tmp_tsetup3, 3, spicef)
-
-            targetLib.print_msg_sim("Last stage precise hold search, timestep: "\
-                +str(targetCell.sim_hold_timestep))
-            ( res_thold3, res_min_prop_in_out, res_min_prop_cin_out, res_min_setup, res_min_hold, res_min_trans_out, \
-                res_energy_start, res_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                res_q_in_dyn, res_q_out_dyn, res_q_clk_dyn, res_q_vdd_dyn, res_q_vss_dyn, \
-                res_i_in_leak, res_i_vdd_leak, res_i_vss_leak) \
-             = holdSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    tmp_thold1 - targetCell.sim_hold_timestep * tmp_tstep_mag2 *3,\
-                                                                    tmp_thold1 + targetCell.sim_hold_timestep * tmp_tstep_mag2 *1 ,\
-                                                                    targetCell.sim_hold_timestep, tmp_tsetup3, 2, spicef)
-            ## if target is not D2Q (= set or reset), clip lowest hold time to almost zero
-            ## this is because removal simulation sometimes very small 
-
-            # end setup/hold search
-            #res_list_prop.append(res_min_prop_in_out) # store D2Q 
-            res_list_prop.append(res_min_prop_cin_out) # store C2Q (not D2Q)
-            res_list_tran.append(res_min_trans_out)
-            res_list_setup.append(res_min_setup)
-            res_list_hold.append(res_min_hold)
-            print("Final C2Q: "+str(res_min_prop_cin_out)+",setup: "+str(res_min_setup)+", hold: "+str(res_min_hold))
-
-            ## intl. energy calculation
-            ## intl. energy is the sum of short-circuit energy and drain-diffusion charge/discharge energy
-            ## larger Ql: intl. Q, load Q 
-            ## smaller Qs: intl. Q
-            ## Eintl = QsV
-            if(abs(res_q_vdd_dyn) < abs(res_q_vss_dyn)):
-                res_list_eintl.append(abs(res_q_vdd_dyn*targetLib.vdd_voltage*targetLib.energy_meas_high_threshold-\
-                    abs((res_energy_end - res_energy_start)*(abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2* \
-                    (targetLib.vdd_voltage*targetLib.energy_meas_high_threshold))))
-            else:
-                res_list_eintl.append(abs(res_q_vss_dyn*targetLib.vdd_voltage*targetLib.energy_meas_high_threshold-\
-                    abs((res_energy_end - res_energy_start)*(abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2* \
-                    (targetLib.vdd_voltage*targetLib.energy_meas_high_threshold))))
-
-            ## input energy
-            res_list_ein.append(abs(res_q_in_dyn)*targetLib.vdd_voltage)
-            res_list_eclk.append(abs(res_q_clk_dyn)*targetLib.vdd_voltage)
-
-            ## Cin = Qin / V
-            res_list_cin.append(abs(res_q_in_dyn)/(targetLib.vdd_voltage))
-            res_list_cclk.append(abs(res_q_clk_dyn)/(targetLib.vdd_voltage))
-
-            ## Pleak = average of Pleak_vdd and Pleak_vss
-            ## P = I * V
-            res_list_pleak.append((abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2*(targetLib.vdd_voltage)) #
-
-        list2_prop.append(res_list_prop)
-        list2_tran.append(res_list_tran)
-        list2_setup.append(res_list_setup)
-        list2_hold.append(res_list_hold)
-        #list2_estart.append(res_list_estart)
-        #list2_eend.append(res_list_eend)
-        list2_eintl.append(res_list_eintl)
-        list2_ein.append(res_list_ein)
-        list2_cin.append(res_list_cin)
-        list2_eclk.append(res_list_eclk)
-        list2_cclk.append(res_list_cclk)
-        list2_pleak.append(res_list_pleak)
-
-    targetHarness.set_list2_prop(list2_prop)
-    #targetHarness.print_list2_prop(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_prop(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_prop()
-    targetHarness.set_list2_tran(list2_tran)
-    #targetHarness.print_list2_tran(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_tran(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_tran()
-    targetHarness.set_list2_setup(list2_setup)
-    #targetHarness.print_list2_setup(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_setup(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_setup()
-    targetHarness.set_list2_hold(list2_hold)
-    #targetHarness.print_list2_hold(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_hold(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_hold()
-    targetHarness.set_list2_eintl(list2_eintl)
-    #targetHarness.print_list2_eintl(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_eintl(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_eintl()
-    targetHarness.set_list2_ein(list2_ein)
-    #targetHarness.print_list2_ein(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_ein(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_ein()
-    targetHarness.set_list2_cin(list2_cin)
-    #targetHarness.print_list2_cin(targetCell.load, targetCell.in_slopes)
-    targetHarness.average_list2_cin(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_cin()
-    targetHarness.set_list2_eclk(list2_eclk)
-    #targetHarness.print_list2_eclk(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_eclk(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_eclk()
-    targetHarness.set_list2_cclk(list2_cclk)
-    #targetHarness.print_list2_cclk(targetCell.load, targetCell.in_slopes)
-    targetHarness.average_list2_cclk(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_cclk()
-    targetHarness.set_list2_pleak(list2_pleak)
-    #targetHarness.print_list2_pleak(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_pleak(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_pleak()
-
-
-def runSpiceFlopDelayMT(targetLib, targetCell, targetHarness, spicef):
-    list2_prop =   []
-    list2_setup =   []
-    list2_hold =   []
-    list2_tran =   []
-    list2_estart = []
-    list2_eend =   []
-    list2_eintl =   []
-    list2_ein =   []
-    list2_cin =   []
-    list2_eclk =   []
-    list2_cclk =   []
-    list2_pleak =   []
-    for tmp_slope in targetCell.in_slopes:
-        res_list_prop =   []  # C2Q
-        res_list_setup =  []  # D2C(setup)
-        res_list_hold =   []  # C2D(hold)
-        res_list_tran =   []
-        res_list_estart = []
-        res_list_eend =   []
-        res_list_eintl =   []
-        res_list_ein =   []
-        res_list_cin =   []
-        res_list_eclk =   []
-        res_list_cclk =   []
-        res_list_pleak =   []
-
-        for tmp_load in targetCell.load:
-            tmp_max_val_loop = float(targetCell.in_slopes[-1]) * 10 # use x10 of max. slope for max val.
-            tmp_min_setup = tmp_max_val_loop # temporal value for setup 
-            tmp_tsetup1 = tmp_max_val_loop # temporal value for setup 
-            tmp_tsetup2 = tmp_max_val_loop # temporal value for setup 
-            tmp_tsetup3 = tmp_max_val_loop # temporal value for setup 
-            tmp_thold1 = tmp_max_val_loop # temporal value for setup 
-            tmp_thold2 = tmp_max_val_loop # temporal value for setup 
-            tmp_min_hold  = tmp_max_val_loop # temporal value for setup 
-
-            # C2Q and setup search (sparce)
-            # perform two-stage simulation
-            # 1st stage: sim w/  10-% output swing
-            # 2nd stage: sim w/ 100-% output swing
-            tsimendmag = [1, 10]; # magnify parameter of _tsimend
-            tranmag = [float(targetLib.logic_threshold_low)*1.1, 1];         # magnify parameter of transient simulation
-            tmp_min_prop_in_out   = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_min_prop_cin_out  = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_min_trans_out     = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_energy_start  = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_energy_end    = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_energy_clk_start  = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_energy_clk_end    = tmp_max_val_loop # temporal value for D2Qmin search
-
-            tmp_tstep_mag = 20
-            tmp_tstep_mag1 = float(targetCell.in_slopes[-1])/float(targetCell.in_slopes[0])* tmp_tstep_mag
-            targetLib.print_msg_sim("First stage sparse setup search, timestep: "\
-                +str(targetCell.sim_setup_timestep*tmp_tstep_mag1))
-            ( tmp_tsetup1, tmp_min_prop_in_out, _, _, _, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, _, _, _, _, _, _, _, _, _, _) \
-             = setupSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    targetCell.sim_setup_lowest, targetCell.sim_setup_highest, \
-                                                                    targetCell.sim_setup_timestep*tmp_tstep_mag1, tmp_min_hold, 5, spicef)
-
-            tmp_tstep_mag = 2
-            tmp_tstep_mag2 = tmp_tstep_mag1 
-            tmp_tstep_mag1 = tmp_tstep_mag1 / tmp_tstep_mag
-            targetLib.print_msg_sim("First stage sparse setup search, timestep: "\
-                +str(targetCell.sim_setup_timestep*tmp_tstep_mag1))
-            ( tmp_tsetup1, tmp_min_prop_in_out, _, _, _, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, _, _, _, _, _, _, _, _, _, _) \
-             = setupSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    tmp_tsetup1 - targetCell.sim_setup_timestep * tmp_tstep_mag2 *1,\
-                                                                    tmp_tsetup1 + targetCell.sim_setup_timestep * tmp_tstep_mag2 *2,\
-                                                                    targetCell.sim_setup_timestep * tmp_tstep_mag1, tmp_min_hold, 3, spicef)
-
-            while(tmp_tstep_mag1 > tmp_tstep_mag ):
-                tmp_tstep_mag2 = tmp_tstep_mag1 
-                tmp_tstep_mag1 = tmp_tstep_mag1 / tmp_tstep_mag
-                targetLib.print_msg_sim("Mid. stage precise setup search, timestep: "\
-                    +str(targetCell.sim_setup_timestep*tmp_tstep_mag1))
-                ( tmp_tsetup1, tmp_min_prop_in_out, _, _, _, tmp_min_trans_out, \
-                    tmp_energy_start, tmp_energy_end, _, _, _, _, _, _, _, _, _, _) \
-                 = setupSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                        tmp_tsetup1 - targetCell.sim_setup_timestep * tmp_tstep_mag2  *1,\
-                                                                        tmp_tsetup1 + targetCell.sim_setup_timestep * tmp_tstep_mag2  *3,\
-                                                                        targetCell.sim_setup_timestep * tmp_tstep_mag1, tmp_min_hold, 3, spicef)
-
-            targetLib.print_msg_sim("Last stage precise setup search, timestep: "\
-                +str(targetCell.sim_setup_timestep))
-            ( tmp_tsetup3, tmp_min_prop_in_out, _, _, _, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, _, _, _, _, _, _, _, _, _, _) \
-             = setupSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    tmp_tsetup1 - targetCell.sim_setup_timestep * tmp_tstep_mag2 *1,\
-                                                                    tmp_tsetup1 + targetCell.sim_setup_timestep * tmp_tstep_mag2 *3,\
-                                                                    targetCell.sim_setup_timestep, tmp_min_hold, 2, spicef)
+            v0, v1 = "'_vdd'", "'_vss'" # Fall from vdd to vss
+        outlines.append(f"VIN VIN 0 PWL(0 {v0} '_tstart1' {v0} '_tstart2' {v1} '_tend1' {v1} '_tend2' {v0} '_tsimend' {v0})")
+    else:
+        # If not targeted, hold stable
+        for port, value in zip(harness.stable_in_ports, harness.stable_in_port_values):
+            outlines.append("VIN VIN 0 DC " + ("'_vdd'" if value == '1' else "'_vss'"))
     
-            ## if target is D2Q, do standard setup/hold search
-            #if((targetHarness.target_inport_val == "01")or(targetHarness.target_inport_val == "10")):
-            tmp_tstep_mag = 20
-            tmp_tstep_mag1 = float(targetCell.in_slopes[-1])/float(targetCell.in_slopes[0])* tmp_tstep_mag
-            targetLib.print_msg_sim("First stage sparse hold search, timestep: "\
-                +str(targetCell.sim_hold_timestep*tmp_tstep_mag1))
-            ( tmp_thold1, tmp_min_prop_in_out,tmp_min_prop_cin_out, tmp_min_setup, tmp_min_hold, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                tmp_q_in_dyn, tmp_q_out_dyn, tmp_q_clk_dyn, tmp_q_vdd_dyn, tmp_q_vss_dyn, \
-                tmp_i_in_leak, tmp_i_vdd_leak, tmp_i_vss_leak) \
-            = holdSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    targetCell.sim_hold_lowest, targetCell.sim_hold_highest, \
-                                                                    targetCell.sim_hold_timestep*tmp_tstep_mag1, tmp_tsetup3, \
-                                                                    3, spicef)
-
-            tmp_tstep_mag = 2
-            tmp_tstep_mag2 = tmp_tstep_mag1
-            tmp_tstep_mag1 = tmp_tstep_mag1 /  tmp_tstep_mag
-            targetLib.print_msg_sim("First stage sparse hold search, timestep: "\
-                +str(targetCell.sim_setup_timestep*tmp_tstep_mag1))
-            ( tmp_thold1, tmp_min_prop_in_out,tmp_min_prop_cin_out, tmp_min_setup, tmp_min_hold, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                tmp_q_in_dyn, tmp_q_out_dyn, tmp_q_clk_dyn, tmp_q_vdd_dyn, tmp_q_vss_dyn, \
-                tmp_i_in_leak, tmp_i_vdd_leak, tmp_i_vss_leak) \
-             = holdSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    tmp_thold1 - targetCell.sim_hold_timestep * tmp_tstep_mag2 *2,\
-                                                                    tmp_thold1 + targetCell.sim_hold_timestep * tmp_tstep_mag2 *1,\
-                                                                    targetCell.sim_hold_timestep * tmp_tstep_mag1, tmp_tsetup3, 3, spicef)
-
-            while(tmp_tstep_mag1 > tmp_tstep_mag ):
-                tmp_tstep_mag2 = tmp_tstep_mag1
-                tmp_tstep_mag1 = tmp_tstep_mag1 /  tmp_tstep_mag
-                targetLib.print_msg_sim("Mid. stage precise hold search, timestep: "\
-                    +str(targetCell.sim_setup_timestep*tmp_tstep_mag1))
-                ( tmp_thold1, tmp_min_prop_in_out,tmp_min_prop_cin_out, tmp_min_setup, tmp_min_hold, tmp_min_trans_out, \
-                    tmp_energy_start, tmp_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                    tmp_q_in_dyn, tmp_q_out_dyn, tmp_q_clk_dyn, tmp_q_vdd_dyn, tmp_q_vss_dyn, \
-                    tmp_i_in_leak, tmp_i_vdd_leak, tmp_i_vss_leak) \
-                 = holdSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                        tmp_thold1 - targetCell.sim_hold_timestep * tmp_tstep_mag2 *3 ,\
-                                                                        tmp_thold1 + targetCell.sim_hold_timestep * tmp_tstep_mag2 *1 ,\
-                                                                        targetCell.sim_hold_timestep * tmp_tstep_mag1, tmp_tsetup3, 3, spicef)
-
-            targetLib.print_msg_sim("Last stage precise hold search, timestep: "\
-                +str(targetCell.sim_hold_timestep))
-            ( res_thold3, res_min_prop_in_out, res_min_prop_cin_out, res_min_setup, res_min_hold, res_min_trans_out, \
-                res_energy_start, res_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                res_q_in_dyn, res_q_out_dyn, res_q_clk_dyn, res_q_vdd_dyn, res_q_vss_dyn, \
-                res_i_in_leak, res_i_vdd_leak, res_i_vss_leak) \
-             = holdSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    tmp_thold1 - targetCell.sim_hold_timestep * tmp_tstep_mag2 *3,\
-                                                                    tmp_thold1 + targetCell.sim_hold_timestep * tmp_tstep_mag2 *1 ,\
-                                                                    targetCell.sim_hold_timestep, tmp_tsetup3, 2, spicef)
-            ## if target is not D2Q (= set or reset), clip lowest hold time to almost zero
-            ## this is because removal simulation sometimes very small 
-
-            # end setup/hold search
-            #res_list_prop.append(res_min_prop_in_out) # store D2Q 
-            res_list_prop.append(res_min_prop_cin_out) # store C2Q (not D2Q)
-            res_list_tran.append(res_min_trans_out)
-            res_list_setup.append(res_min_setup)
-            res_list_hold.append(res_min_hold)
-            print("Final C2Q: "+str(res_min_prop_cin_out)+",setup: "+str(res_min_setup)+", hold: "+str(res_min_hold))
-
-            ## intl. energy calculation
-            ## intl. energy is the sum of short-circuit energy and drain-diffusion charge/discharge energy
-            ## larger Ql: intl. Q, load Q 
-            ## smaller Qs: intl. Q
-            ## Eintl = QsV
-            if(abs(res_q_vdd_dyn) < abs(res_q_vss_dyn)):
-                res_list_eintl.append(abs(res_q_vdd_dyn*targetLib.vdd_voltage*targetLib.energy_meas_high_threshold-\
-                    abs((res_energy_end - res_energy_start)*(abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2* \
-                    (targetLib.vdd_voltage*targetLib.energy_meas_high_threshold))))
+    # Clock input: VCIN
+    # The original code also includes provisions for single clock pulses e.g. 010, but they are never used
+    # TODO: Evaluate whether we need to include logic for single clock pulse ops
+    if harness.timing_type_clock == 'falling_edge':
+        v0, v1 = "'_vss'", "'_vdd'"
+    else:
+        v0, v1 = "'_vdd'", "'_vss'"
+    outlines.append(f"VCIN VCIN 0 PWL(0 {v0} '_tclk1' {v0} '_tclk2' {v1} '_tclk3' {v1} '_tclk4' {v0} '_tclk6' {v0} '_tclk7' {v1} '_tsimend' {v1})")
+
+    # Reset
+    # Note that this assumes active low reset, so rise = high-to-low
+    if harness.reset:
+        if harness.reset_direction:
+            # Reset is rising or falling
+            target_vname = 'VRIN'
+            if harness.reset_direction == 'rise':
+                v0, v1 = "'_vdd'", "'_vss'"
             else:
-                res_list_eintl.append(abs(res_q_vss_dyn*targetLib.vdd_voltage*targetLib.energy_meas_high_threshold-\
-                    abs((res_energy_end - res_energy_start)*(abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2* \
-                    (targetLib.vdd_voltage*targetLib.energy_meas_high_threshold))))
-
-            ## input energy
-            res_list_ein.append(abs(res_q_in_dyn)*targetLib.vdd_voltage)
-            res_list_eclk.append(abs(res_q_clk_dyn)*targetLib.vdd_voltage)
-
-            ## Cin = Qin / V
-            res_list_cin.append(abs(res_q_in_dyn)/(targetLib.vdd_voltage))
-            res_list_cclk.append(abs(res_q_clk_dyn)/(targetLib.vdd_voltage))
-
-            ## Pleak = average of Pleak_vdd and Pleak_vss
-            ## P = I * V
-            res_list_pleak.append((abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2*(targetLib.vdd_voltage)) #
-
-        list2_prop.append(res_list_prop)
-        list2_tran.append(res_list_tran)
-        list2_setup.append(res_list_setup)
-        list2_hold.append(res_list_hold)
-        #list2_estart.append(res_list_estart)
-        #list2_eend.append(res_list_eend)
-        list2_eintl.append(res_list_eintl)
-        list2_ein.append(res_list_ein)
-        list2_cin.append(res_list_cin)
-        list2_eclk.append(res_list_eclk)
-        list2_cclk.append(res_list_cclk)
-        list2_pleak.append(res_list_pleak)
-
-    targetHarness.set_list2_prop(list2_prop)
-    #targetHarness.print_list2_prop(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_prop(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_prop()
-    targetHarness.set_list2_tran(list2_tran)
-    #targetHarness.print_list2_tran(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_tran(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_tran()
-    targetHarness.set_list2_setup(list2_setup)
-    #targetHarness.print_list2_setup(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_setup(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_setup()
-    targetHarness.set_list2_hold(list2_hold)
-    #targetHarness.print_list2_hold(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_hold(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_hold()
-    targetHarness.set_list2_eintl(list2_eintl)
-    #targetHarness.print_list2_eintl(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_eintl(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_eintl()
-    targetHarness.set_list2_ein(list2_ein)
-    #targetHarness.print_list2_ein(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_ein(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_ein()
-    targetHarness.set_list2_cin(list2_cin)
-    #targetHarness.print_list2_cin(targetCell.load, targetCell.in_slopes)
-    targetHarness.average_list2_cin(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_cin()
-    targetHarness.set_list2_eclk(list2_eclk)
-    #targetHarness.print_list2_eclk(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_eclk(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_eclk()
-    targetHarness.set_list2_cclk(list2_cclk)
-    #targetHarness.print_list2_cclk(targetCell.load, targetCell.in_slopes)
-    targetHarness.average_list2_cclk(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_cclk()
-    targetHarness.set_list2_pleak(list2_pleak)
-    #targetHarness.print_list2_pleak(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_pleak(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_pleak()
-        
-
-def runSpiceFlopRecoveryRemoval(targetLib, targetCell, targetHarness, spicef):
-    ## Note! In recovery/removal simulation, recovaly is treated as setup,
-    ## and removal is treated as hold. Set/reset to Q delay is treated 
-    ## as D2Q delay.
-    ## VIN in spice is connected to set/reset, and fixed wave is applied
-    ## into d-input of Flip-Flop
-    list2_prop =   []
-    list2_setup =   []
-    list2_hold =   []
-    list2_tran =   []
-    list2_estart = []
-    list2_eend =   []
-    list2_eintl =   []
-    list2_ein =   []
-    list2_cin =   []
-    list2_eclk =   []
-    list2_cclk =   []
-    list2_pleak =   []
-    for tmp_slope in targetCell.in_slopes:
-        res_list_prop =   []  # C2Q
-        res_list_setup =  []  # D2C(setup)
-        res_list_hold =   []  # C2D(hold)
-        res_list_tran =   []
-        res_list_estart = []
-        res_list_eend =   []
-        res_list_eintl =   []
-        res_list_ein =   []
-        res_list_cin =   []
-        res_list_eclk =   []
-        res_list_cclk =   []
-        res_list_pleak =   []
-
-        for tmp_load in targetCell.load:
-            tmp_max_val_loop = float(targetCell.in_slopes[-1]) * 10 # use x10 of max. slope for max val.
-            tmp_min_setup = tmp_max_val_loop # temporal value for setup 
-            tmp_tsetup1 = tmp_max_val_loop   # temporal value for setup 
-            tmp_tsetup2 = tmp_max_val_loop   # temporal value for setup 
-            tmp_tsetup3 = tmp_max_val_loop   # temporal value for setup 
-            tmp_thold1 = tmp_max_val_loop    # temporal value for setup 
-            tmp_thold2 = tmp_max_val_loop    # temporal value for setup 
-            tmp_min_hold  = tmp_max_val_loop # temporal value for setup 
-
-            # C2Q and recovery search (sparce)
-            # perform two-stage simulation
-            # 1st stage: sim w/  10-% output swing
-            # 2nd stage: sim w/ 100-% output swing
-            tsimendmag = [1, 10]; # magnify parameter of _tsimend
-            tranmag = [float(targetLib.logic_threshold_low)*1.1, 1];         # magnify parameter of transient simulation
-            tmp_min_prop_in_out   = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_min_prop_cin_out  = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_min_trans_out     = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_energy_start  = tmp_max_val_loop     # temporal value for D2Qmin search
-            tmp_energy_end    = tmp_max_val_loop     # temporal value for D2Qmin search
-            tmp_energy_clk_start  = tmp_max_val_loop # temporal value for D2Qmin search
-            tmp_energy_clk_end    = tmp_max_val_loop # temporal value for D2Qmin search
-            spicefrr = spicef + "_rr"
-
-            ## for recovery search, invert reset/set signal 
-            targetHarness.invert_set_reset_val()
-
-            tmp_tstep_mag = 20
-            tmp_tstep_mag1 = float(targetCell.in_slopes[-1])/float(targetCell.in_slopes[0])* tmp_tstep_mag
-            targetLib.print_msg_sim("First stage sparse recovery search, timestep: "\
-                +str(targetCell.sim_setup_timestep*tmp_tstep_mag1))
-            ( tmp_tsetup1, tmp_min_prop_in_out, _, _, _, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, _, _, _, _, _, _, _, _, _, _) \
-             = setupSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    targetCell.sim_setup_lowest, targetCell.sim_setup_highest*2, \
-                                                                    targetCell.sim_setup_timestep*tmp_tstep_mag1, tmp_min_hold, 3, spicefrr)
-
-            tmp_tstep_mag = 2
-            tmp_tstep_mag2 = tmp_tstep_mag1 
-            tmp_tstep_mag1 = tmp_tstep_mag1 /  tmp_tstep_mag
-            targetLib.print_msg_sim("First stage sparse recovery search, timestep: "\
-                +str(targetCell.sim_setup_timestep*tmp_tstep_mag2))
-            ( tmp_tsetup1, tmp_min_prop_in_out, _, _, _, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, _, _, _, _, _, _, _, _, _, _) \
-             = setupSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    tmp_tsetup1 - targetCell.sim_setup_timestep * tmp_tstep_mag2 *1,\
-                                                                    tmp_tsetup1 + targetCell.sim_setup_timestep * tmp_tstep_mag2 *3,\
-                                                                    targetCell.sim_setup_timestep * tmp_tstep_mag2, tmp_min_hold, 3, spicefrr)
-
-            while(tmp_tstep_mag1 > tmp_tstep_mag ):
-                tmp_tstep_mag2 = tmp_tstep_mag1 
-                tmp_tstep_mag1 = tmp_tstep_mag1 /  tmp_tstep_mag
-                targetLib.print_msg_sim("Mid. stage precise recovery search, timestep: "\
-                    +str(targetCell.sim_setup_timestep*tmp_tstep_mag2))
-                ( tmp_tsetup1, tmp_min_prop_in_out, _, _, _, tmp_min_trans_out, \
-                    tmp_energy_start, tmp_energy_end, _, _, _, _, _, _, _, _, _, _) \
-                 = setupSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                        tmp_tsetup1 - targetCell.sim_setup_timestep * tmp_tstep_mag2 *1,\
-                                                                        tmp_tsetup1 + targetCell.sim_setup_timestep * tmp_tstep_mag2 *3,\
-                                                                        targetCell.sim_setup_timestep * tmp_tstep_mag2, tmp_min_hold, 3, spicefrr)
-
-            targetLib.print_msg_sim("Last stage precise recovery search, timestep: "+str(targetCell.sim_setup_timestep))
-            ( res_tsetup3, res_min_prop_in_out, res_min_prop_cin_out, res_min_setup, res_min_hold, res_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, _, _, _, _, _, _, _, _, _, _) \
-             = setupSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    tmp_tsetup1 - targetCell.sim_setup_timestep * tmp_tstep_mag2 *1,\
-                                                                    tmp_tsetup1 + targetCell.sim_setup_timestep * tmp_tstep_mag2 *3,\
-                                                                    targetCell.sim_setup_timestep, tmp_min_hold, 2, spicefrr)
-    
-            ## Note! min_setup (min R2C) is recovery.
-            ## It is stored into setup object, and 
-            ## stored into recovery LUT 
-            res_list_setup.append(res_min_setup) # recovery
-
-            ## for removal search, restore reset/set signal 
-            targetHarness.invert_set_reset_val()
-
-            tmp_tstep_mag = 20
-            tmp_tstep_mag1 = float(targetCell.in_slopes[-1])/float(targetCell.in_slopes[0])* tmp_tstep_mag
-            targetLib.print_msg_sim("First stage sparse removal search, timestep: "\
-                +str(targetCell.sim_hold_timestep*tmp_tstep_mag1))
-            ( tmp_thold1, tmp_min_prop_in_out,tmp_min_prop_cin_out, tmp_min_setup, tmp_min_hold, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                tmp_q_in_dyn, tmp_q_out_dyn, tmp_q_clk_dyn, tmp_q_vdd_dyn, tmp_q_vss_dyn, \
-                tmp_i_in_leak, tmp_i_vdd_leak, tmp_i_vss_leak) \
-            = holdSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    targetCell.sim_hold_highest * 4, 0 , \
-                                                                    -targetCell.sim_hold_timestep*tmp_tstep_mag1, -tmp_tsetup3, \
-                                                                    3, spicefrr)
-
-            tmp_tstep_mag = 2
-            tmp_tstep_mag2 = tmp_tstep_mag1 
-            tmp_tstep_mag1 = tmp_tstep_mag1 /  tmp_tstep_mag
-            targetLib.print_msg_sim("Mid. stage precise removal search, timestep: "\
-                +str(targetCell.sim_hold_timestep*tmp_tstep_mag2))
-            ( tmp_thold1, tmp_min_prop_in_out,tmp_min_prop_cin_out, tmp_min_setup, tmp_min_hold, tmp_min_trans_out, \
-                tmp_energy_start, tmp_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                tmp_q_in_dyn, tmp_q_out_dyn, tmp_q_clk_dyn, tmp_q_vdd_dyn, tmp_q_vss_dyn, \
-                tmp_i_in_leak, tmp_i_vdd_leak, tmp_i_vss_leak) \
-             = holdSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    tmp_thold1 + targetCell.sim_hold_timestep * tmp_tstep_mag2 *2,\
-                                                                    tmp_thold1 - targetCell.sim_hold_timestep * tmp_tstep_mag2 *2,\
-                                                                    -targetCell.sim_hold_timestep * tmp_tstep_mag2, -tmp_tsetup3, 3, spicefrr)
-
-            while(tmp_tstep_mag1 > tmp_tstep_mag ):
-                tmp_tstep_mag2 = tmp_tstep_mag1 
-                tmp_tstep_mag1 = tmp_tstep_mag1 /  tmp_tstep_mag
-                targetLib.print_msg_sim("Mid. stage precise removal search, timestep: "\
-                    +str(targetCell.sim_hold_timestep*tmp_tstep_mag2))
-                #print("tmp_thold1: "+str(tmp_thold1))
-                #print("from: "+str(tmp_thold1 + targetCell.sim_hold_timestep * tmp_tstep_mag2 *3)+" to: "+str(tmp_thold1 - targetCell.sim_hold_timestep * tmp_tstep_mag2 *2))
-                ( tmp_thold1, tmp_min_prop_in_out,tmp_min_prop_cin_out, tmp_min_setup, tmp_min_hold, tmp_min_trans_out, \
-                    tmp_energy_start, tmp_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                    tmp_q_in_dyn, tmp_q_out_dyn, tmp_q_clk_dyn, tmp_q_vdd_dyn, tmp_q_vss_dyn, \
-                    tmp_i_in_leak, tmp_i_vdd_leak, tmp_i_vss_leak) \
-                 = holdSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                        tmp_thold1 + targetCell.sim_hold_timestep * tmp_tstep_mag2 *3,\
-                                                                        tmp_thold1 - targetCell.sim_hold_timestep * tmp_tstep_mag2 *2,\
-                                                                        -targetCell.sim_hold_timestep * tmp_tstep_mag2, -tmp_tsetup3, 3, spicefrr)
-
-            targetLib.print_msg_sim("Last stage precise removal search, timestep: "\
-                +str(targetCell.sim_hold_timestep))
-            ( res_thold3, res_min_prop_in_out, res_min_prop_cin_out, res_min_setup, res_min_hold, res_min_trans_out, \
-                res_energy_start, res_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                res_q_in_dyn, res_q_out_dyn, res_q_clk_dyn, res_q_vdd_dyn, res_q_vss_dyn, \
-                res_i_in_leak, res_i_vdd_leak, res_i_vss_leak) \
-             = holdSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-                                                                    tmp_thold1 + targetCell.sim_hold_timestep * tmp_tstep_mag2 *3,\
-                                                                    tmp_thold1 - targetCell.sim_hold_timestep * tmp_tstep_mag2 *1,\
-                                                                    -targetCell.sim_hold_timestep, -tmp_tsetup3, 2, spicefrr)
-    
-
-            # end recov/remov search
-            #res_list_prop.append(res_min_prop_in_out) # store D2Q 
-            res_list_prop.append(res_min_prop_cin_out) # store C2Q (cause Q is activated by set/reset)
-            res_list_tran.append(res_min_trans_out)
-            ## Note! min_backside_setup = hold (min minus R2Q) is removal.
-            ## It is stored into hold object, and 
-            ## stored into removal LUT 
-            res_list_hold.append(-res_min_setup)   # removal
-
-            ## intl. energy calculation
-            ## intl. energy is the sum of short-circuit energy and drain-diffusion charge/discharge energy
-            ## larger Ql: intl. Q, load Q 
-            ## smaller Qs: intl. Q
-            ## Eintl = QsV
-            if(abs(res_q_vdd_dyn) < abs(res_q_vss_dyn)):
-                res_list_eintl.append(abs(res_q_vdd_dyn*targetLib.vdd_voltage*targetLib.energy_meas_high_threshold-\
-                    abs((res_energy_end - res_energy_start)*(abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2* \
-                    (targetLib.vdd_voltage*targetLib.energy_meas_high_threshold))))
-            else:
-                res_list_eintl.append(abs(res_q_vss_dyn*targetLib.vdd_voltage*targetLib.energy_meas_high_threshold-\
-                    abs((res_energy_end - res_energy_start)*(abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2* \
-                    (targetLib.vdd_voltage*targetLib.energy_meas_high_threshold))))
-
-            ## input energy
-            res_list_ein.append(abs(res_q_in_dyn)*targetLib.vdd_voltage)
-            res_list_eclk.append(abs(res_q_clk_dyn)*targetLib.vdd_voltage)
-
-            ## Cin = Qin / V
-            res_list_cin.append(abs(res_q_in_dyn)/(targetLib.vdd_voltage))
-            res_list_cclk.append(abs(res_q_clk_dyn)/(targetLib.vdd_voltage))
-
-            ## Pleak = average of Pleak_vdd and Pleak_vss
-            ## P = I * V
-            res_list_pleak.append((abs(res_i_vdd_leak)+abs(res_i_vdd_leak))/2*(targetLib.vdd_voltage)) #
-
-        list2_prop.append(res_list_prop)
-        list2_tran.append(res_list_tran)
-        list2_setup.append(res_list_setup)
-        list2_hold.append(res_list_hold)
-        #list2_estart.append(res_list_estart)
-        #list2_eend.append(res_list_eend)
-        list2_eintl.append(res_list_eintl)
-        list2_ein.append(res_list_ein)
-        list2_cin.append(res_list_cin)
-        list2_eclk.append(res_list_eclk)
-        list2_cclk.append(res_list_cclk)
-        list2_pleak.append(res_list_pleak)
-
-    targetHarness.set_list2_prop(list2_prop)
-    #targetHarness.print_list2_prop(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_prop(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_prop()
-    targetHarness.set_list2_tran(list2_tran)
-    #targetHarness.print_list2_tran(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_tran(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_tran()
-    targetHarness.set_list2_setup(list2_setup)
-    #targetHarness.print_list2_setup(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_setup(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_setup()
-    targetHarness.set_list2_hold(list2_hold)
-    #targetHarness.print_list2_hold(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_hold(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_hold()
-    targetHarness.set_list2_eintl(list2_eintl)
-    #targetHarness.print_list2_eintl(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_eintl(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_eintl()
-    targetHarness.set_list2_ein(list2_ein)
-    #targetHarness.print_list2_ein(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_ein(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_ein()
-    targetHarness.set_list2_cin(list2_cin)
-    #targetHarness.print_list2_cin(targetCell.load, targetCell.in_slopes)
-    targetHarness.average_list2_cin(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_cin()
-    targetHarness.set_list2_eclk(list2_eclk)
-    #targetHarness.print_list2_eclk(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_eclk(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_eclk()
-    targetHarness.set_list2_cclk(list2_cclk)
-    #targetHarness.print_list2_cclk(targetCell.load, targetCell.in_slopes)
-    targetHarness.average_list2_cclk(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_cclk()
-    targetHarness.set_list2_pleak(list2_pleak)
-    #targetHarness.print_list2_pleak(targetCell.load, targetCell.in_slopes)
-    targetHarness.write_list2_pleak(targetLib, targetCell.load, targetCell.in_slopes)
-    #targetHarness.print_lut_pleak()
-
-def holdSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, \
-            thold_lowest, thold_highest, thold_tstep, tsetup, timestep_mag, spicef):
-    # hold search
-    # perform two-stage simulation
-    # 1st stage: sim w/  10-% output swing
-    # 2nd stage: sim w/ 100-% output swing
-    tsimendmag = [1, 10]; # magnify parameter of _tsimend
-    tranmag = [float(targetLib.logic_threshold_low)*1.1, 1];  # magnify parameter of transient simulation
-    tmp_max_val_loop = float(targetCell.in_slopes[-1]) * 40 # use x10 of max. slope for max val.
-    tmp_min_setup = tmp_max_val_loop # temporal value for setup 
-    tmp_min_prop_in_out   = tmp_max_val_loop # temporal value for D2Qmin search
-    tmp_min_prop_cin_out  = tmp_max_val_loop # temporal value for D2Qmin search
-    tmp_min_trans_out     = tmp_max_val_loop # temporal value for D2Qmin search
-    tmp_energy_start  = tmp_max_val_loop # temporal value for D2Qmin search
-    tmp_energy_end    = tmp_max_val_loop # temporal value for D2Qmin search
-    
-    ## calculate whole slope length from logic threshold
-    tmp_slope_mag = 1 / (targetLib.logic_threshold_high - targetLib.logic_threshold_low)
-
-    #print ("debug "+str(thold_highest)+","+str(thold_lowest)+","+str(thold_tstep)+"\n\n")
-    for thold in np.arange (thold_highest, thold_lowest*1.01, -thold_tstep):
-        first_stage_fail = 0
-        for j in range(len(tranmag)):
-            if(first_stage_fail == 0):
-                targetLib.print_msg_sim("dSetup: "+str(f'{tsetup:,.4f}')+str(targetLib.time_unit)+" dHold: "+str(f'{thold:,.4f}')+str(targetLib.time_unit)+" stage:"+str(j))
-                cap_line = ".param cap ="+str(tmp_load)+str(targetLib.units.capacitance.symbol)+"\n"
-                slew_line = ".param slew ="+str(tmp_slope*tmp_slope_mag)+str(targetLib.time_unit)+"\n"
-                cslew_line = ".param cslew ="+str(targetCell.cslope)+str(targetLib.time_unit)+"\n"
-                tunit_line = ".param tunit ="+str(targetCell.in_slopes[-1])+str(targetLib.time_unit)+"\n"
-                tsetup_line = ".param tsetup ="+str(tsetup)+str(targetLib.time_unit)+"\n"
-                thold_line = ".param thold ="+str(thold)+str(targetLib.time_unit)+"\n"
-                tsimend_line = ".param tsimendmag ="+str(tsimendmag[j])+" tranmag ="+str(tranmag[j])+"\n"
-                spicefo = str(spicef)+"_j"+str(j)+"_"+str(tmp_load)+"_"+str(tmp_slope)+"_setup"+str(f'{tsetup:,.4f}')+"_hold"+str(f'{thold:,.4f}')+".sp"
-                tran_line =".tran "+str(targetCell.sim_timestep*timestep_mag)+str(targetLib.time_unit)+" '_tsimend'\n"
-                #targetLib.print_msg(spicefo)
-        
-                ## Delay simulation
-                (res_prop_in_out, res_prop_cin_out, res_trans_out,res_energy_start, \
-                res_energy_end, res_energy_clk_start, res_energy_clk_end, res_setup, res_hold)\
-                    = genFileFlop_trial1(targetLib, targetCell, targetHarness, "delay", \
-                        cap_line, slew_line, cslew_line, tunit_line, tsetup_line, \
-                        thold_line, tsimend_line, tran_line, "none", "none", "none", "none", spicefo)
-          
-                ## if delay simulation failes, skip energy simulation
-                ## and set fail
-                if((str(res_energy_start) == "failed") or (str(res_energy_end) == "failed") \
-                    or (str(res_energy_clk_start) == "failed") or (str(res_energy_clk_end) == "failed")):
-                    first_stage_fail = 1
-                else:
-                # Energy simulation
-                    estart_line = ".param ENERGY_START = "+str(res_energy_start)+"\n"
-                    eend_line = ".param ENERGY_END = "+str(res_energy_end)+"\n"
-                    eclkstart_line = ".param ENERGY_CLK_START = "+str(res_energy_clk_start)+"\n"
-                    eclkend_line = ".param ENERGY_CLK_END = "+str(res_energy_clk_end)+"\n"
-                    spicefo = str(spicef)+"_j"+str(j)+"_"+str(tmp_load)+"_"+str(tmp_slope)+"_setup"+str(f'{tsetup:,.4f}')+"_hold"+str(f'{thold:,.4f}')+"_2.sp"
-                    (res_prop_in_out, res_prop_cin_out, res_trans_out, res_setup, res_hold,\
-                                q_in_dyn, q_out_dyn, q_clk_dyn, q_vdd_dyn, q_vss_dyn, i_in_leak, \
-                                i_vdd_leak, i_vss_leak) \
-                        = genFileFlop_trial1(targetLib, targetCell, targetHarness, "energy", cap_line, slew_line, cslew_line,\
-                                                                    tunit_line, tsetup_line, thold_line, tsimend_line, tran_line, \
-                                                                    estart_line, eend_line, eclkstart_line, eclkend_line,  spicefo)
-                    #  check 1st and 2nd run of simulation
-                    # if res_trans_out failed, it may failed in both run -> exit 
-                    if(res_trans_out == "failed"):
-                        first_stage_fail = 1
-
-        #  check second run of simulation
-        # if (current D2Q > prev. D2Q), exceeds min D2Q
-        #if((res_prop_in_out == "failed")or(float(res_prop_in_out) > tmp_min_prop_in_out)or(first_stage_fail == 1)):
-        if((res_prop_in_out == "failed")or(first_stage_fail == 1)):
-            if(tmp_max_val_loop == tmp_min_prop_in_out):
-                print("Error: simulation failed! Check spice deck!")
-                print("spice deck: "+spicefo)
-                exit()
-
-            targetLib.print_msg_sim("Min. D2Q found. Break loop at dHold: "+str(f'{thold:,.4f}'))
-
-            return ( float(thold + thold_tstep), tmp_min_prop_in_out, tmp_min_prop_cin_out, tmp_min_setup, tmp_min_hold, tmp_min_trans_out, \
-            #return ( float(thold + thold_tstep), tmp_min_prop_in_out, tmp_min_prop_cin_out, float(res_setup), tmp_min_hold, tmp_min_trans_out, \
-                            tmp_energy_start, tmp_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                            tmp_q_in_dyn, tmp_q_out_dyn, tmp_q_clk_dyn,  tmp_q_vdd_dyn, tmp_q_vss_dyn, \
-                            tmp_i_in_leak, tmp_i_vdd_leak, tmp_i_vss_leak)
-#			break
-        
-        #update C2Q(res_prop_in_out) 
-        tmp_min_prop_in_out  = float(res_prop_in_out)
-        # in set/reset sim, res_prop_cin_out is not measured
-        if((res_prop_cin_out == "failed") or (res_prop_cin_out == tmp_max_val_loop)):
-            tmp_min_prop_cin_out = float(res_prop_in_out)
+                v0, v1 = "'_vss'", "'_vdd'"
+            outlines.append(f"VRIN VRIN 0 PWL(0 {v0} '_tstart1' {v0} '_tstart2' {v1} '_tend1' {v1} '_tend2' {v0} '_tsimend' {v0})")
         else:
-            tmp_min_prop_cin_out  = float(res_prop_cin_out)
-        tmp_min_trans_out    = float(res_trans_out)
-        if(res_energy_start != "failed"):
-            tmp_energy_start = float(res_energy_start)
-        if(res_energy_end != "failed"):
-            tmp_energy_end   = float(res_energy_end)
-        #targetLib.print_msg("res_energy_clk_start;"+res_energy_clk_start)
-        if(res_energy_clk_start != "failed"):
-            tmp_energy_clk_start = float(res_energy_clk_start)
-        if(res_energy_clk_end != "failed"):
-            tmp_energy_clk_end   = float(res_energy_clk_end)
-        if(q_in_dyn != "failed"):
-            tmp_q_in_dyn   = float(q_in_dyn)
-        if(q_out_dyn != "failed"):
-            tmp_q_out_dyn  = float(q_out_dyn)
-        if(q_clk_dyn != "failed"):
-            tmp_q_clk_dyn  = float(q_clk_dyn)
-        if(q_vdd_dyn != "failed"):
-            tmp_q_vdd_dyn  = float(q_vdd_dyn)
-        if(q_vss_dyn != "failed"):
-            tmp_q_vss_dyn  = float(q_vss_dyn)
-        if(q_in_dyn != "failed"):
-            tmp_i_in_leak  = float(i_in_leak)
-        if(q_vdd_dyn != "failed"):
-            tmp_i_vdd_leak = float(i_vdd_leak)
-        if(q_vss_dyn != "failed"):
-            tmp_i_vss_leak = float(i_vss_leak)
-        if(res_setup != "failed"):
-            tmp_min_setup = float(res_setup)
-        if(res_hold != "failed"):
-            tmp_min_hold = float(res_hold)
-            #targetLib.print_msg("tmp_min_hold: "+str(tmp_min_hold)+"\n")
-        #targetLib.print_msg("spicef: "+str(spicef)+"\n")
-
-    # finish without premature ending
-    print("Error! End of dhold search!!: "+str(f'{thold:,.4f}'))
-    print("spice deck: "+spicefo)
-    exit()
-
-
-def setupSearchFlop(targetLib, targetCell, targetHarness, tmp_load, tmp_slope, tsetup_lowest, tsetup_highest, tsetup_tstep, tmp_min_hold, timestep_mag, spicef):
-    tsimendmag = [1, 10]; # magnify parameter of _tsimend
-    tranmag = [float(targetLib.logic_threshold_low)*1.1, 1];         # magnify parameter of transient simulation
-    tmp_max_val_loop = float(targetCell.in_slopes[-1]) * 10 # use x10 of max. slope for max val.
-    tmp_min_setup = tmp_max_val_loop # temporal value for setup 
-    tmp_min_prop_in_out   = tmp_max_val_loop # temporal value for D2Qmin search
-    tmp_min_prop_cin_out  = tmp_max_val_loop # temporal value for D2Qmin search
-    tmp_min_trans_out     = tmp_max_val_loop # temporal value for D2Qmin search
-    tmp_energy_start  = tmp_max_val_loop # temporal value for D2Qmin search
-    tmp_energy_end    = tmp_max_val_loop # temporal value for D2Qmin search
-
-    ## calculate whole slope length from logic threshold
-    tmp_slope_mag = 1 / (targetLib.logic_threshold_high - targetLib.logic_threshold_low)
-
-    for tsetup in np.arange (tsetup_lowest, tsetup_highest*1.01, tsetup_tstep):
-        first_stage_fail = 0
-        for j in range(len(tranmag)):
-            if(first_stage_fail == 0):
-                targetLib.print_msg_sim("dSetup: "+str(f'{tsetup:,.4f}')+str(targetLib.time_unit)+" dHold: "+str(f'{tmp_min_hold:,.4f}')+str(targetLib.time_unit)+" stage:"+str(j))
-                cap_line = ".param cap ="+str(tmp_load)+str(targetLib.units.capacitance.symbol)+"\n"
-                slew_line = ".param slew ="+str(tmp_slope*tmp_slope_mag)+str(targetLib.time_unit)+"\n"
-                cslew_line = ".param cslew ="+str(targetCell.cslope)+str(targetLib.time_unit)+"\n"
-                tunit_line = ".param tunit ="+str(targetCell.in_slopes[-1])+str(targetLib.time_unit)+"\n"
-                tsetup_line = ".param tsetup ="+str(tsetup)+str(targetLib.time_unit)+"\n"
-                thold_line = ".param thold ="+str(tmp_min_hold)+str(targetLib.time_unit)+"\n"
-                tsimend_line = ".param tsimendmag ="+str(tsimendmag[j])+" tranmag ="+str(tranmag[j])+"\n"
-                spicefo = str(spicef)+"_j"+str(j)+"_"+str(tmp_load)+"_"+str(tmp_slope)+"_setup"+str(f'{tsetup:,.4f}')+"_hold"+str(f'{tmp_min_hold:,.4f}')+".sp"
-                tran_line =".tran "+str(targetCell.sim_timestep*timestep_mag)+str(targetLib.time_unit)+" '_tsimend'\n"
-                #targetLib.print_msg_sim(spicefo)
-        
-                ## Delay simulation
-                (res_prop_in_out, res_prop_cin_out, res_trans_out, res_energy_start, \
-                res_energy_end, res_energy_clk_start, res_energy_clk_end, res_setup, res_hold)\
-                    = genFileFlop_trial1(targetLib, targetCell, targetHarness, "delay", 
-                        cap_line, slew_line, cslew_line,	tunit_line, tsetup_line, \
-                        thold_line, tsimend_line, tran_line, "none", "none", "none", "none", spicefo)
-          
-                ## if delay simulation failes, skip energy simulation
-                ## and set fail
-                if((str(res_energy_start) == "failed") or (str(res_energy_end) == "failed") \
-                    or (str(res_energy_clk_start) == "failed") or (str(res_energy_clk_end) == "failed")):
-                    first_stage_fail = 1
-                else:
-                    ## Energy simulation 
-                    estart_line = ".param ENERGY_START = "+str(res_energy_start)+"\n"
-                    eend_line = ".param ENERGY_END = "+str(res_energy_end)+"\n"
-                    eclkstart_line = ".param ENERGY_CLK_START = "+str(res_energy_clk_start)+"\n"
-                    eclkend_line = ".param ENERGY_CLK_END = "+str(res_energy_clk_end)+"\n"
-                    spicefo = str(spicef)+"_j"+str(j)+"_"+str(tmp_load)+"_"+str(tmp_slope)+"_setup"+str(f'{tsetup:,.4f}')+"_hold"+str(f'{tmp_min_hold:,.4f}')+"_2.sp"
-                    (res_prop_in_out, res_prop_cin_out, res_trans_out, res_setup, res_hold,\
-                                q_in_dyn, q_out_dyn, q_clk_dyn, q_vdd_dyn, q_vss_dyn, i_in_leak, \
-                                i_vdd_leak, i_vss_leak) \
-                        = genFileFlop_trial1(targetLib, targetCell, targetHarness, "energy", cap_line, slew_line, cslew_line,\
-                                                                    tunit_line, tsetup_line, thold_line, tsimend_line, tran_line, \
-                                                                    estart_line, eend_line, eclkstart_line, eclkend_line,  spicefo)
-                    ##  check 1st and 2nd run of simulation
-                    ## if res_trans_out failed, it may failed in both run -> exit 
-                    if(res_trans_out == "failed"):
-                        first_stage_fail = 1
-
-        tmp_tsetup = tsetup - tsetup_tstep # restore previous tsetup 
-
-        #  check second run of simulation
-        # if (current D2Q > prev. D2Q), exceeds min D2Q
-        if((res_prop_in_out == "failed") \
-            or(float(res_prop_in_out) > tmp_min_prop_in_out) \
-            or(first_stage_fail == 1)):
-            if(tmp_max_val_loop == tmp_min_prop_in_out):
-                targetLib.print_msg_sim("Error: simulation failed! Check spice deck!")
-                targetLib.print_msg("spice deck: "+spicefo)
-                exit()
-            targetLib.print_msg_sim("Min. D2Q found. Break loop at dSetup: "+str(f'{tsetup:,.4f}'))
-            # finish without premature ending
-            #return float(tsetup - tsetup_tstep) 
-            return ( float(tsetup - tsetup_tstep), tmp_min_prop_in_out, tmp_min_prop_cin_out,\
-                            tmp_min_setup, tmp_min_hold, tmp_min_trans_out, \
-                            tmp_energy_start, tmp_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                            tmp_q_in_dyn, tmp_q_out_dyn, tmp_q_clk_dyn,  tmp_q_vdd_dyn, tmp_q_vss_dyn, \
-                            tmp_i_in_leak, tmp_i_vdd_leak, tmp_i_vss_leak)
-
-        # update C2Q(res_prop_in_out) 
-        # tmp_min_prop_in_out  = float(res_prop_in_out)
-        #update C2Q(res_prop_in_out) 
-        tmp_min_prop_in_out  = float(res_prop_in_out)
-        # in set/reset sim, res_prop_cin_out is not measured
-        if((res_prop_cin_out == "failed") or (res_prop_cin_out == tmp_max_val_loop)):
-            tmp_min_prop_cin_out = float(res_prop_in_out)
-        else:
-            tmp_min_prop_cin_out  = float(res_prop_cin_out)
-        tmp_min_trans_out    = float(res_trans_out)
-        if(res_energy_start != "failed"):
-            tmp_energy_start = float(res_energy_start)
-        if(res_energy_end != "failed"):
-            tmp_energy_end   = float(res_energy_end)
-        #targetLib.print_msg_sim("res_energy_clk_start;"+res_energy_clk_start)
-        if(res_energy_clk_start != "failed"):
-            tmp_energy_clk_start = float(res_energy_clk_start)
-        if(res_energy_clk_end != "failed"):
-            tmp_energy_clk_end   = float(res_energy_clk_end)
-        if(q_in_dyn != "failed"):
-            tmp_q_in_dyn   = float(q_in_dyn)
-        if(q_out_dyn != "failed"):
-            tmp_q_out_dyn  = float(q_out_dyn)
-        if(q_clk_dyn != "failed"):
-            tmp_q_clk_dyn  = float(q_clk_dyn)
-        if(q_vdd_dyn != "failed"):
-            tmp_q_vdd_dyn  = float(q_vdd_dyn)
-        if(q_vss_dyn != "failed"):
-            tmp_q_vss_dyn  = float(q_vss_dyn)
-        if(q_in_dyn != "failed"):
-            tmp_i_in_leak  = float(i_in_leak)
-        if(q_vdd_dyn != "failed"):
-            tmp_i_vdd_leak = float(i_vdd_leak)
-        if(q_vss_dyn != "failed"):
-            tmp_i_vss_leak = float(i_vss_leak)
-        if(res_setup != "failed"):
-            tmp_min_setup = float(res_setup)
-#		if(res_hold != "failed"):         ## In the setup search  
-#			tmp_min_hold = float(res_hold)  ## hold should be ideal
-
-    # finish without premature ending
-    targetLib.print_msg_sim("Min. D2Q found at dSetup: "+str(f'{tsetup:,.4f}'))
-    #return float(tsetup - tsetup_tstep) 
-    return ( float(tsetup - tsetup_tstep), tmp_min_prop_in_out, tmp_min_prop_cin_out, \
-                    tmp_min_setup, tmp_min_hold, tmp_min_trans_out, \
-                    tmp_energy_start, tmp_energy_end, tmp_energy_clk_start, tmp_energy_clk_end, \
-                    tmp_q_in_dyn, tmp_q_out_dyn, tmp_q_clk_dyn,  tmp_q_vdd_dyn, tmp_q_vss_dyn, \
-                    tmp_i_in_leak, tmp_i_vdd_leak, tmp_i_vss_leak)
-
-
-def genFileFlop_trial1(targetLib, targetCell, targetHarness, sim_mode, cap_line, \
-        slew_line, cslew_line, tunit_line, tsetup_line, thold_line, tsimend_line, \
-        tran_line, estart_line, eend_line, eclkstart_line, eclkend_line, spicef):
-    #print (spicef)
-    #print ("generate AND2\n")
-    #targetLib.print_msg_sim(dir(targetLib))
-    spicelis = spicef
-    spicelis += ".lis"
-
-    # if file is not exist, create spice file and run spice
-    if(not os.path.isfile(spicelis)):
-#		print("skip file: "+str(spicef))
-#	else:
-        with open(spicef,'w') as f:
-            outlines = []
-            outlines.append("*title: flop delay meas.\n")
-            outlines.append(".option brief nopage nomod post=1 ingold=2 autostop\n")
-            outlines.append(".inc '../"+str(targetCell.model)+"'\n")
-            outlines.append(".inc '../"+str(targetCell.netlist)+"'\n")
-            outlines.append(".param _vdd = '"+str(targetLib.vdd_voltage)+"*"+str(targetLib.voltage_mag)+"'\n")
-            outlines.append(".param _vss = '"+str(targetLib.vss_voltage)+"*"+str(targetLib.voltage_mag)+"'\n")
-            outlines.append(".param _vnw = '"+str(targetLib.nwell_voltage)+"*"+str(targetLib.voltage_mag)+"'\n")
-            outlines.append(".param _vpw = '"+str(targetLib.pwell_voltage)+"*"+str(targetLib.voltage_mag)+"'\n")
-            outlines.append(".param cap = 10f \n")
-            outlines.append(".param slew = 100p \n")
-            outlines.append(".param cslew = 100p \n")
-            outlines.append(".param tunit = 100p \n")
-            outlines.append(".param tsetup = 100p \n")
-            outlines.append(".param thold = 100p \n")
-            outlines.append(".param tsimendmag = 100 tranmag = 1\n")
-            outlines.append(".param _tslew = slew \n")
-            outlines.append(".param _tclk1 = slew \n")                # ^ first clock
-            outlines.append(".param _tclk2 = '_tclk1 + cslew '\n")    # | 
-            outlines.append(".param _tclk3 = '_tclk2 + tunit '\n")    # | 
-            outlines.append(".param _tclk4 = '_tclk3 + cslew '\n")    # v ^ Recovery 
-            outlines.append(".param _tclk5 = '_tclk4 + tunit '\n")    #   V Removal    
-            outlines.append(".param _tstart1 = '_tclk5 + tunit * 10 + tsetup'\n")    # ^ data input start 
-            outlines.append(".param _tstart2 = '_tstart1 + slew'\n")                 # v varied w/ dedge
-            outlines.append(".param _tend1 = '_tstart2 + tunit + thold'\n")   # ^ data input end
-            outlines.append(".param _tend2 = '_tend1 + slew'\n")      # v varied w/ dedge
-            outlines.append(".param _tclk6 = '_tclk4 + tunit * 10'\n")       # ^ second clock
-            outlines.append(".param _tclk7 = '_tclk6 + cslew '\n")           # v 
-            outlines.append(".param _tsimend = '_tend2 + tunit * 50 * tsimendmag' \n")
-            outlines.append(".param _Energy_meas_end_extent = "+str(targetLib.energy_meas_time_extent)+"\n")
-            outlines.append(".measure Tran tstart2_to_tend1 param='_tend1 - _tstart2'\n") # flag to judge min. pulse input for hold search 
-            outlines.append(" \n")
-            outlines.append("VDD_DYN VDD_DYN 0 DC '_vdd' \n")
-            outlines.append("VSS_DYN VSS_DYN 0 DC '_vss' \n")
-            outlines.append("VNW_DYN VNW_DYN 0 DC '_vnw' \n")
-            outlines.append("VPW_DYN VPW_DYN 0 DC '_vpw' \n")
-            outlines.append("VDD_LEAK VDD_LEAK 0 DC '_vdd' \n")
-            outlines.append("VSS_LEAK VSS_LEAK 0 DC '_vss' \n")
-            outlines.append("VNW_LEAK VNW_LEAK 0 DC '_vnw' \n")
-            outlines.append("VPW_LEAK VPW_LEAK 0 DC '_vpw' \n")
-            outlines.append("* output load calculation\n")
-            outlines.append("VOCAP VOUT WOUT DC 0\n")
-            outlines.append(" \n")
-            # in auto mode, simulation timestep is 1/10 of min. input slew
-            # simulation runs 1000x of input slew time
-            # outlines.append(".tran "+str(targetCell.sim_timestep)+str(targetLib.time_unit)+" '_tsimend'\n")
-            outlines.append( tran_line )
-            outlines.append(" \n")
-            # target Vinput
-            V_in_target = None 
-            
-            # DATA
-            if(targetHarness.target_inport_val == "01"):
-                outlines.append("VIN VIN 0 PWL(0 '_vss' '_tstart1' '_vss' '_tstart2' '_vdd' '_tend1' '_vdd' '_tend2' '_vss' '_tsimend' '_vss') \n")
-                V_in_target = 'VIN'
-            elif(targetHarness.target_inport_val == "10"):
-                outlines.append("VIN VIN 0 PWL(0 '_vdd' '_tstart1' '_vdd' '_tstart2' '_vss' '_tend1' '_vss' '_tend2' '_vdd' '_tsimend' '_vdd') \n")
-                V_in_target = 'VIN'
-            elif((targetHarness.target_inport_val == "1") or (targetHarness.target_inport_val == "11")):
-                outlines.append("VIN VIN 0 DC '_vdd' \n")
-                #outlines.append("VIN VIN 0 PWL(0 '_vss' '_tclk4' '_vss' '_tclk5' '_vdd') \n")
-            elif((targetHarness.target_inport_val == "0") or (targetHarness.target_inport_val == "00")):
-                outlines.append("VIN VIN 0 DC '_vss' \n")
-                #outlines.append("VIN VIN 0 PWL(0 '_vdd' '_tclk4' '_vdd' '_tclk5' '_vss') \n")
-            else:
-                targetLib.print_msg("Error: no VIN difinition!")
-                exit()
-            outlines.append("VHIGH VHIGH 0 DC '_vdd' \n")
-            outlines.append("VLOW VLOW 0 DC '_vss' \n")
-  
-            ## CLOCK
-            ## two clock pulses are used (one for set init., another for C2Q)
-            if(targetHarness.target_clock_val == "0101"):
-                outlines.append("VCIN VCIN 0 PWL(0 '_vss' '_tclk1' '_vss' '_tclk2' '_vdd' '_tclk3' '_vdd' '_tclk4' '_vss' '_tclk6' '_vss' '_tclk7' '_vdd' '_tsimend' '_vdd') \n")
-                ## V_in_target = 'VCIN'
-            elif(targetHarness.target_clock_val == "1010"):
-                outlines.append("VCIN VCIN 0 PWL(0 '_vdd' '_tclk1' '_vdd' '_tclk2' '_vss' '_tclk3' '_vss' '_tclk4' '_vdd' '_tclk6' '_vdd' '_tclk7' '_vss' '_tsimend' '_vss') \n")
-                ## V_in_target = 'VCIN'
-            ## one clock pulse is used (for set init.)
-            elif(targetHarness.target_clock_val == "010"):
-                outlines.append("VCIN VCIN 0 PWL(0 '_vss' '_tclk1' '_vss' '_tclk2' '_vdd' '_tclk3' '_vdd' '_tclk4' '_vss' '_tsimend' '_vss') \n")
-            elif(targetHarness.target_clock_val == "101"):
-                outlines.append("VCIN VCIN 0 PWL(0 '_vdd' '_tclk1' '_vdd' '_tclk2' '_vss' '_tclk3' '_vss' '_tclk4' '_vdd' '_tsimend' '_vdd') \n")
-            else:
-                targetLib.print_error("Error: no VCIN difinition!")
-            
-            ## RST
-            if(targetHarness.target_reset_val == "01"):
-                outlines.append("VRIN VRIN 0 PWL(0 '_vdd' '_tstart1' '_vdd' '_tstart2' '_vss' '_tend1' '_vss' '_tend2' '_vdd' '_tsimend' '_vdd') \n")
-                V_in_target = 'VRIN'
-            elif(targetHarness.target_reset_val == "10"):
-                outlines.append("VRIN VRIN 0 PWL(0 '_vss' '_tstart1' '_vss' '_tstart2' '_vdd' '_tend1' '_vdd' '_tend2' '_vss' '_tsimend' '_vss') \n")
-                V_in_target = 'VRIN'
-            elif((targetHarness.target_reset_val == "11")or(targetHarness.target_reset_val == "1")):
-                outlines.append("VRIN VRIN 0 DC '_vdd' \n")
-            elif((targetHarness.target_reset_val == "00")or(targetHarness.target_reset_val == "0")):
-                outlines.append("VRIN VRIN 0 DC '_vss' \n")
-            elif(targetHarness.target_reset != None):
-                targetLib.print_error("Error: Reset is difined as "+str(targetHarness.target_reset)+" but not VRIN is not defined!")
-  
-            ## SET
-            if(targetHarness.target_set_val == "01"):
-                outlines.append("VSIN VSIN 0 PWL(0 '_vdd' '_tstart1' '_vdd' '_tstart2' '_vss' '_tend1' '_vss' '_tend2' '_vdd' '_tsimend' '_vdd') \n")
-                V_in_target = 'VSIN'
-            elif(targetHarness.target_set_val == "10"):
-                outlines.append("VSIN VSIN 0 PWL(0 '_vss' '_tstart1' '_vss' '_tstart2' '_vdd' '_tend1' '_vdd' '_tend2' '_vss' '_tsimend' '_vss') \n")
-                V_in_target = 'VSIN'
-            elif((targetHarness.target_set_val == "11")or(targetHarness.target_set_val == "1")):
-                outlines.append("VSIN VSIN 0 DC '_vdd' \n")
-            elif((targetHarness.target_set_val == "00")or(targetHarness.target_set_val == "0")):
-                outlines.append("VSIN VSIN 0 DC '_vss' \n")
-            elif(targetHarness.target_set != None):
-                targetLib.print_error("Error: Set is difined as "+str(targetHarness.target_set)+" but not VSIN is not defined!")
-  
-            # candidate of input: D, RST, SET
-            # measure D2Q
-            outlines.append("** Delay \n")
-            outlines.append("* Prop delay (D2Q)\n")
-            ## case, input 01 -> output 10
-            if(((targetHarness.target_inport_val == "01")or(targetHarness.target_set_val == "01")or(targetHarness.target_reset_val == "01"))and(targetHarness.target_outport_val == "10")):
-                outlines.append(".measure Tran PROP_IN_OUT trig v("+V_in_target+") val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' rise=1 \n")
-                outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' fall=1  \n") 
-                outlines.append(".measure Tran TRANS_OUT trig v(VOUT) val='"+str(float(targetLib.logic_threshold_high)*float(targetLib.vdd_voltage))+"' td='_tclk5' fall=1\n")
-                outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_threshold_low)*float(targetLib.vdd_voltage ))+"' fall=1  \n")
-                ##outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_threshold_low)*float(targetLib.vdd_voltage ))+"*tranmag' fall=1  \n")
-                #outlines.append(".measure Tran ENERGY_START when v("+V_in_target+")='"+str(targetLib.energy_meas_low_threshold)+"' rise=1 td='_tclk5'\n")
-                #outlines.append(".measure Tran ENERGY_END when v(VOUT)='"+str(targetLib.energy_meas_low_threshold)+"' fall=1 td='_tclk5' \n")
-            ## case, input 01 -> output 01
-            elif(((targetHarness.target_inport_val == "01")or(targetHarness.target_set_val == "01")or(targetHarness.target_reset_val == "01"))and(targetHarness.target_outport_val == "01")):
-                outlines.append(".measure Tran PROP_IN_OUT trig v("+V_in_target+") val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' rise=1 \n")
-                outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' rise=1  \n") 
-                outlines.append(".measure Tran TRANS_OUT trig v(VOUT) val='"+str(float(targetLib.logic_threshold_low)*float(targetLib.vdd_voltage))+"' td='_tclk5' rise=1\n")
-                outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_threshold_high)*float(targetLib.vdd_voltage ))+"' rise=1  \n")
-                #outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_threshold_high)*float(targetLib.vdd_voltage ))+"*tranmag' rise=1  \n")
-                #outlines.append(".measure Tran ENERGY_START when v("+V_in_target+")='"+str(targetLib.energy_meas_low_threshold)+"' rise=1  td='_tclk5'\n")
-                #outlines.append(".measure Tran ENERGY_END when v(VOUT)='"+str(targetLib.energy_meas_high_threshold)+"' rise=1  td='_tclk5'\n")
-            ## case, input 10 -> output 01
-            elif(((targetHarness.target_inport_val == "10")or(targetHarness.target_set_val == "10")or(targetHarness.target_reset_val == "10"))and(targetHarness.target_outport_val == "01")):
-                outlines.append(".measure Tran PROP_IN_OUT trig v("+V_in_target+") val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' fall=1 \n")
-                outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' rise=1  \n")
-                outlines.append(".measure Tran TRANS_OUT trig v(VOUT) val='"+str(float(targetLib.logic_threshold_low)*float(targetLib.vdd_voltage))+"' td='_tclk5' rise=1\n")
-                outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_threshold_high)*float(targetLib.vdd_voltage ))+"' rise=1  \n")
-                #outlines.append(".measure Tran ENERGY_START when v("+V_in_target+")='"+str(targetLib.energy_meas_high_threshold)+"' fall=1  td='_tclk5'\n")
-                #outlines.append(".measure Tran ENERGY_END when v(VOUT)='"+str(targetLib.energy_meas_high_threshold)+"' rise=1  td='_tclk5'\n")
-            ## case, input 10 -> output 10
-            elif(((targetHarness.target_inport_val == "10")or(targetHarness.target_set_val == "10")or(targetHarness.target_reset_val == "10"))and(targetHarness.target_outport_val == "10")):
-                outlines.append(".measure Tran PROP_IN_OUT trig v("+V_in_target+") val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' fall=1 \n")
-                outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' fall=1  \n")
-                outlines.append(".measure Tran TRANS_OUT trig v(VOUT) val='"+str(float(targetLib.logic_threshold_high)*float(targetLib.vdd_voltage))+"' td='_tclk5' fall=1\n")
-                outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_threshold_low)*float(targetLib.vdd_voltage ))+"' fall=1 \n")
-                #outlines.append(".measure Tran ENERGY_START when v("+V_in_target+")='"+str(targetLib.energy_meas_high_threshold)+"' fall=1  td='_tclk5'\n")
-                #outlines.append(".measure Tran ENERGY_END when v(VOUT)='"+str(targetLib.energy_meas_low_threshold)+"' fall=1  td='_tclk5'\n")
-            else:
-                print ("Target input of DFF is not registered for characterization!, die!")
-                print ("inport_val:"+str(targetHarness.target_inport_val))
-                print ("set_val:"+str(targetHarness.target_set_val))
-                print ("reset_val:"+str(targetHarness.target_reset_val))
-                print ("outport_val:"+str(targetHarness.target_outport_val))
-                exit()
-  
-            outlines.append("* Prop delay (C2Q)\n")
-            ## case, clock 01 -> output 10
-            if((targetHarness.target_clock_val == "0101")and((targetHarness.target_outport_val == "10")or(targetHarness.target_set_val == "10")or(targetHarness.target_reset_val == "10"))):
-                outlines.append(".measure Tran PROP_CIN_OUT trig v(VCIN) val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' rise=1 \n") # meas. 2nd clock
-                outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' fall=1 \n") 
-            ## case, clock 01 -> output 01
-            elif((targetHarness.target_clock_val == "0101")and((targetHarness.target_outport_val == "01")or(targetHarness.target_set_val == "01")or(targetHarness.target_reset_val == "01"))):
-                outlines.append(".measure Tran PROP_CIN_OUT trig v(VCIN) val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' rise=1 \n") # meas. 2nd clock
-                outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' rise=1 \n") 
-            ## case, clock 10 -> output 10
-            elif((targetHarness.target_clock_val == "1010")and((targetHarness.target_outport_val == "10")or(targetHarness.target_set_val == "10")or(targetHarness.target_reset_val == "10"))):
-                outlines.append(".measure Tran PROP_CIN_OUT trig v(VCIN) val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' fall=1 \n") # meas. 2nd clock
-                outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' fall=1 \n") 
-            ## case, clock 10 -> output 01
-            elif((targetHarness.target_clock_val == "1010")and((targetHarness.target_outport_val == "01")or(targetHarness.target_set_val == "01")or(targetHarness.target_reset_val == "01"))):
-                outlines.append(".measure Tran PROP_CIN_OUT trig v(VCIN) val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' fall=1 \n") # meas. 2nd clock
-                outlines.append("+ targ v(VOUT) val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' rise=1 \n") 
-  
-            # measure D2C(setup)
-            outlines.append("* Prop delay (D2C,setup)\n")
-            # case, D 01 -> CLK 01
-            if(((targetHarness.target_inport_val == "01")or(targetHarness.target_set_val == "01")or(targetHarness.target_reset_val == "01"))and(targetHarness.target_clock_val == "0101")):
-                outlines.append(".measure Tran PROP_IN_D2C trig v("+V_in_target+") val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' rise=1 \n")
-                outlines.append("+ targ v(VCIN) val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' rise=1 \n") # meas. 2nd clock  
-            # case, D 10 -> CLK 01
-            elif(((targetHarness.target_inport_val == "10")or(targetHarness.target_set_val == "10")or(targetHarness.target_reset_val == "10"))and(targetHarness.target_clock_val == "0101")):
-                outlines.append(".measure Tran PROP_IN_D2C trig v("+V_in_target+") val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' fall=1 \n")
-                outlines.append("+ targ v(VCIN) val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' rise=1 \n") # meas. 2nd clock  
-            # case, D 01 -> CLK 10
-            elif(((targetHarness.target_inport_val == "01")or(targetHarness.target_set_val == "01")or(targetHarness.target_reset_val == "01"))and(targetHarness.target_clock_val == "1010")):
-                outlines.append(".measure Tran PROP_IN_D2C trig v("+V_in_target+") val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' rise=1 \n")
-                outlines.append("+ targ v(VCIN) val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' fall=1 \n") # meas. 2nd clock  
-            # case, D 10 -> CLK 10
-            elif(((targetHarness.target_inport_val == "10")or(targetHarness.target_set_val == "10")or(targetHarness.target_reset_val == "10"))and(targetHarness.target_clock_val == "1010")):
-                outlines.append(".measure Tran PROP_IN_D2C trig v("+V_in_target+") val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' fall=1 \n")
-                outlines.append("+ targ v(VCIN) val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' fall=1 \n") # meas. 2nd clock  
-            #else:
-                #print ("Skip D2C(setup) simulation")
-  
-            # measure C2D(HOLD)
-            outlines.append("* Prop delay (C2D,HOLD)\n")
-            
-            # case, CLK 01 -> D (01->)10 
-            if(((targetHarness.target_inport_val == "01")or(targetHarness.target_set_val == "01")or(targetHarness.target_reset_val == "01"))and(targetHarness.target_clock_val == "0101")):
-                outlines.append(".measure Tran PROP_IN_C2D trig v(VCIN) val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' rise=1 \n") # meas. 2nd clock
-                outlines.append("+ targ v("+V_in_target+") val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' fall=1 \n") 
-            # case, CLK 01 -> D (10->)01 
-            elif(((targetHarness.target_inport_val == "10")or(targetHarness.target_set_val == "10")or(targetHarness.target_reset_val == "10"))and(targetHarness.target_clock_val == "0101")):
-                outlines.append(".measure Tran PROP_IN_C2D trig v(VCIN) val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' rise=1 \n") # meas. 2nd clock
-                outlines.append("+ targ v("+V_in_target+") val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' rise=1 \n") 
-            # case, CLK 10 -> D (01->)10 
-            elif(((targetHarness.target_inport_val == "01")or(targetHarness.target_set_val == "01")or(targetHarness.target_reset_val == "01"))and(targetHarness.target_clock_val == "1010")):
-                outlines.append(".measure Tran PROP_IN_C2D trig v(VCIN) val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' fall=1 \n") # meas. 2nd clock
-                outlines.append("+ targ v("+V_in_target+") val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' fall=1'\n") 
-            # case, CLK 10 -> D (10->)01 
-            elif(((targetHarness.target_inport_val == "10")or(targetHarness.target_set_val == "10")or(targetHarness.target_reset_val == "10"))and(targetHarness.target_clock_val == "1010")):
-                outlines.append(".measure Tran PROP_IN_C2D trig v(VCIN) val='"+str(float(targetLib.logic_high_to_low_threshold)*float(targetLib.vdd_voltage))+"' td='_tclk5' fall=1 \n") # meas. 2nd clock
-                outlines.append("+ targ v("+V_in_target+") val='"+str(float(targetLib.logic_low_to_high_threshold)*float(targetLib.vdd_voltage))+"' rise=1\n") 
-            #else:
-                #print ("Skip C2D(hold) simulation")
-  
-            # get ENERGY_START and ENERGY_END for energy calculation in 2nd round 
-            if(sim_mode == "delay"):
-                outlines.append("* For energy calculation \n")
-                if((targetHarness.target_inport_val == "01")or(targetHarness.target_set_val == "01")or(targetHarness.target_reset_val == "01")):
-                    outlines.append(".measure Tran ENERGY_START when v("+V_in_target+")='"+str(targetLib.energy_meas_low_threshold_voltage)+"' rise=1 \n")
-                elif((targetHarness.target_inport_val == "10")or(targetHarness.target_set_val == "10")or(targetHarness.target_reset_val == "10")):
-                    outlines.append(".measure Tran ENERGY_START when v("+V_in_target+")='"+str(targetLib.energy_meas_high_threshold_voltage)+"' fall=1 \n")
-                if(targetHarness.target_outport_val == "01"):
-                    outlines.append(".measure Tran ENERGY_END when v(VOUT)='"+str(targetLib.energy_meas_high_threshold_voltage)+"' rise=1 \n")
-                elif(targetHarness.target_outport_val == "10"):
-                    outlines.append(".measure Tran ENERGY_END when v(VOUT)='"+str(targetLib.energy_meas_low_threshold_voltage)+"' fall=1 \n")
-                if(targetHarness.target_clock_val == "0101"):
-                    outlines.append(".measure Tran ENERGY_CLK_START when v(VCIN)='"+str(targetLib.energy_meas_low_threshold_voltage)+"'  rise=1 \n")
-                    outlines.append(".measure Tran ENERGY_CLK_end   when v(VCIN)='"+str(targetLib.energy_meas_high_threshold_voltage)+"' rise=1 \n")
-                elif(targetHarness.target_clock_val == "1010"):
-                    outlines.append(".measure Tran ENERGY_CLK_START when v(VCIN)='"+str(targetLib.energy_meas_high_threshold_voltage)+"' fall=1 \n")
-                    outlines.append(".measure Tran ENERGY_CLK_end   when v(VCIN)='"+str(targetLib.energy_meas_low_threshold_voltage)+"'  fall=1 \n")
-  
-            ##
-            ## energy measurement 
-            elif(sim_mode == "energy"):
-                outlines.append(estart_line)
-                outlines.append(eend_line)
-                outlines.append(eclkstart_line)
-                outlines.append(eclkend_line)
-                outlines.append("* \n")
-                outlines.append("** In/Out Q, Capacitance \n")
-                outlines.append("* \n")
-                outlines.append(".measure Tran Q_IN_DYN integ i("+V_in_target+") from='ENERGY_START' to='ENERGY_END'  \n")
-                outlines.append(".measure Tran Q_OUT_DYN integ i(VOCAP) from='ENERGY_START' to='ENERGY_END*_Energy_meas_end_extent' \n")
-                outlines.append(".measure Tran Q_CLK_DYN integ i(VCIN) from='ENERGY_CLK_START' to='ENERGY_CLK_END'  \n")
-                outlines.append(" \n")
-                outlines.append("* \n")
-                outlines.append("** Energy \n")
-                outlines.append("*  (Total charge, Short-Circuit Charge) \n")
-                outlines.append(".measure Tran Q_VDD_DYN integ i(VDD_DYN) from='ENERGY_START' to='ENERGY_END*_Energy_meas_end_extent'  \n")
-                outlines.append(".measure Tran Q_VSS_DYN integ i(VSS_DYN) from='ENERGY_START' to='ENERGY_END*_Energy_meas_end_extent'  \n")
-                outlines.append(" \n")
-                outlines.append("* Leakage current \n")
-                outlines.append(".measure Tran I_VDD_LEAK avg i(VDD_DYN) from='_tstart1*0.1' to='_tstart1'  \n")
-                outlines.append(".measure Tran I_VSS_LEAK avg i(VSS_DYN) from='_tstart1*0.1' to='_tstart1'  \n")
-                outlines.append(" \n")
-                outlines.append("* Gate leak current \n")
-                outlines.append(".measure Tran I_IN_LEAK avg i(VIN) from='_tstart1*0.1' to='_tstart1'  \n")
+            # Reset is static
+            outlines.append("VRIN VRIN 0 DC " + ("'_vdd'" if harness.reset_state == '1' else "'_vss'"))
+
+    # Set
+    # Note that this assumes active low set, so rise = high-to-low
+    if harness.set:
+        if harness.set_direction:
+            # Set is rising or falling
+            target_vname = 'VSIN'
+            if harness.set_direction == 'rise':
+                v0, v1 = "'_vdd'", "'_vss'"
             else:
-                targetLib.print_error("Error, sim_mode should delay/energy/recovery/removal")
-  
-#	#		outlines.append(" \n")
-#	#		outlines.append(" \n")
-            outlines.append("XDFF VIN VCIN VRIN VSIN VOUT VHIGH VLOW VDD_DYN VSS_DYN VNW_DYN VPW_DYN DUT \n")
-            outlines.append("C0 WOUT VSS_DYN 'cap'\n")
-            outlines.append(" \n")
-            outlines.append(".SUBCKT DUT IN CIN RIN SIN OUT HIGH LOW VDD VSS VNW VPW \n")
-            # parse subckt definition
-            tmp_array = targetCell.instance.split()
-            tmp_line = tmp_array[0] # XDUT
-            #targetLib.print_msg_sim(tmp_line)
-            for w1 in tmp_array:
-                # match tmp_array and harness 
-                # search target inport
-                is_matched = 0
-                w2 = targetHarness.target_inport
-                if(w1 == w2):
-                    tmp_line += ' IN'
-                    is_matched += 1
-                # search stable inport
-                for w2 in targetHarness.stable_inport:
-                    if(w1 == w2):
-                        # this is stable inport
-                        # search index for this port
-                        index_val = targetHarness.stable_inport_val[targetHarness.stable_inport.index(w2)]
-                        if(index_val == '1'):
-                            tmp_line += ' HIGH'
-                            is_matched += 1
-                        elif(index_val == '0'):
-                            tmp_line += ' LOW'
-                            is_matched += 1
-                        else:
-                            targetLib.print_msg('Illigal input value for stable input')
-                # search target clock
-                w2 = targetHarness.target_clock
-                if(w1 == w2):
-                    tmp_line += ' CIN'
-                    is_matched += 1
-                # search target reset
-                w2 = targetHarness.target_reset
-                if(w1 == w2):
-                    tmp_line += ' RIN'
+                v0, v1 = "'_vss'", "'_vdd'"
+            outlines.append(f"VSIN VSIN 0 PWL(0 {v0} '_tstart1' {v0} '_tstart2' {v1} '_tend1' {v1} '_tend2' {v0} '_tsimend' {v0})")
+        else:
+            # Set is static
+            outlines.append("VSIN VSIN 0 DC " + ("'_vdd'" if harness.set_state == '1' else "'_vss'"))
+
+    # Measure propagation delays
+    outlines.append('** Delay')
+
+    # D to Q propagation delay
+    outlines.append('* Propagation delay (D to Q)')
+    if harness.in_direction == 'rise':
+        v_in_start = settings.logic_threshold_low_voltage()
+        v_in_end = settings.logic_threshold_high_voltage()
+        vt_in = settings.logic_low_to_high_threshold_voltage()
+        vt_in_inv = settings.logic_high_to_low_threshold_voltage()
+    elif harness.in_direction == 'fall':
+        v_in_start = settings.logic_threshold_high_voltage()
+        v_in_end = settings.logic_threshold_low_voltage()
+        vt_in = settings.logic_high_to_low_threshold_voltage()
+        vt_in_inv = settings.logic_low_to_high_threshold_voltage()
+    else:
+        raise ValueError(f'Unable to configure simulation: no target input pin')
+    if harness.out_direction == 'rise':
+        v_out_start = settings.logic_threshold_low_voltage()
+        v_out_end = settings.logic_threshold_high_voltage()
+        vt_out = settings.logic_low_to_high_threshold_voltage()
+    elif harness.out_direction == 'fall':
+        v_out_start = settings.logic_threshold_high_voltage()
+        v_out_end = settings.logic_threshold_low_voltage()
+        vt_out = settings.logic_high_to_low_threshold_voltage()
+    else:
+        raise ValueError(f'Unable to configure simulation: no target output pin')
+    outlines.extend([
+        f'.measure Tran PROP_IN_OUT trig v("{target_vname}") val=\'{vt_in}\' td=\'_tclk5\' {harness.in_direction}=1',
+        f'+ targ v(VOUT) val=\'{vt_out}\' {harness.out_direction}=1',
+        f'.measure TRAN TRANS_OUT trig v(VOUT) val=\'{v_out_start}\' {harness.out_direction}=1',
+        f'+ targ v(VOUT) val=\'{v_out_end}\' {harness.out_direction}=1'
+    ])
+
+    # C to Q propagation delay
+    outlines.append('* Propagation delay (C to Q)')
+    if harness.timing_sense_clock == 'risiing_edge':
+        clk_direction = 'rise'
+        v_clk_start = settings.logic_threshold_low_voltage()
+        v_clk_end = settings.logic_threshold_high_voltage()
+        vt_clk = settings.logic_low_to_high_threshold_voltage()
+    else:
+        clk_direction = 'fall'
+        v_clk_start = settings.logic_threshold_high_voltage()
+        v_clk_end = settings.logic_threshold_low_voltage()
+        vt_clk = settings.logic_high_to_low_threshold_voltage()
+    outlines.extend([
+        f'.measure Tran PROP_CIN_OUT trig v(VCIN) val=\'{vt_clk}\' td=\'_tclk5\' {clk_direction}=1',
+        f'+ targ v(VOUT) val=\'{vt_in}\' {harness.in_direction}=1'
+    ])
+
+    # D to C setup delay
+    outlines.append('* Setup delay (D to C)')
+    outlines.extend([
+        f'.measure Tran PROP_IN_D2C trig v({target_vname}) val=\'{vt_in}\' td=\'_tclk5\' {harness.in_direction}=1',
+        f'+ targ v(VCIN) val=\'{vt_clk}\' {clk_direction}=1'
+    ])
+
+    # C to D hold delay
+    outlines.append('* Hold delay (C to D)')
+    outlines.extend([
+        f'.measure Tran PROP_IN_C2D trig v(VCIN) val=\'{vt_clk}\' td=\'_tclk5\' {clk_direction}=1',
+        f'+ targ v({target_vname}) val=\'{vt_in_inv} {"rise" if harness.in_direction == "fall" else "fall"}=1'
+    ])
+
+    # Measure energy
+    if not energy:
+        # Measure energy results
+        outlines.extend([
+            f'.measure Tran ENERGY_START when v({target_vname})=\'{v_in_start}\' {harness.in_direction}=1',
+            f'.measure Tran ENERGY_END when v(VOUT)=\'{v_out_end}\' {harness.out_direction}=1',
+            f'.measure Tran ENERGY_CLK_START when v(VCIN)=\'{v_clk_start}\' {clk_direction}=1',
+            f'.measure Tran ENERGY_CLK_START when v(VCIN)=\'{v_clk_end}\' {clk_direction}=1'
+        ])
+    else:
+        # Fetch energy results from previous trial, then measure charge and capacitance
+        outlines.extend([
+            f'.param ENERGY_START = {harness.results[slew][load]["energy_start"]}',
+            f'.param ENERGY_END = {harness.results[slew][load]["energy_end"]}',
+            f'.param ENERGY_CLK_START = {harness.results[slew][load]["energy_clk_start"]}',
+            f'.param ENERGY_CLK_END = {harness.results[slew][load]["energy_clk_end"]}',
+            f'*',
+            f'** In/Out Q, Capacitance',
+            f'*',
+            f'.measure Tran Q_IN_DYN integ i({target_vname}) from=\'ENERGY_START\' to=\'ENERGY_END\'',
+            f'.measure Tran Q_OUT_DYN integ i(VOCAP) from=\'ENERGY_START\' to=\'ENERGY_END*_Energy_meas_end_extent\'',
+            f'.measure Tran Q_CLK_DYN integ i(VCIN) from=\'ENERGY_CLK_START\' to=\'ENERGY_CLK_END\'\n',
+            f'*',
+            f'** Energy',
+            f'*  (Total charge, short-circuit charge)',
+            f'.measure Tran Q_VDD_DYN integ i(VDD_DYN) from=\'ENERGY_START\' to=\'ENERGY_END*_Energy_meas_end_extend\'',
+            f'.measure Tran Q_VSS_DYN integ i(VSS_DYN) from=\'ENERGY_START\' to=\'ENERGY_END*_Energy_meas_end_extent\'\n',
+            f'* Leakage current',
+            f'.measure Tran I_VDD_LEAK avg i(VDD_DYN) from=\'_tstart1*0.1\' to=\'_tstart1\'',
+            f'.measure Tran I_VSS_LEAK avg i(VSS_DYN) from=\'_tstart1*0.1\' to=\'_tstart1\'\n',
+            f'* Gate leak current',
+            f'.measure Tran I_IN_LEAK avg i(VIN) from==\'_tstart1*0.1\' to=\'_tstart1\''
+        ])
+
+    outlines.extend([
+        "XDFF VIN VCIN VRIN VSIN VOUT VHIGH VLOW VDD_DYN VSS_DYN VNW_DYN VPW_DYN DUT",
+        "C0 WOUT VSS_DYN 'cap'",
+        ".SUBCKT DUT IN CIN RIN SIN OUT HIGH LOW VDD VSS VNW VPW"
+    ])
+
+    # Parse subcircuit definition
+    port_list = cell.instance.split()
+    circuit_name = port_list.pop(-1)
+    tmp_line = port_list.pop(0)
+    for port in port_list:
+        # Check target inport
+        is_matched = 0
+        if port == harness.target_in_port:
+            tmp_line += ' IN'
+            is_matched += 1
+        # Check stable inports
+        for stable_port, state in zip(harness.stable_in_ports, harness.stable_in_port_states):
+            if port == stable_port:
+                if state == '1':
+                    tmp_line += ' HIGH'
                     is_matched += 1
-                # search target set
-                w2 = targetHarness.target_set
-                if(w1 == w2):
-                    tmp_line += ' SIN'
+                elif state == '0':
+                    tmp_line += ' LOW'
                     is_matched += 1
-                # search target outport
-                for w2 in targetHarness.target_outport:
-                    if(w1 == w2):
-                        tmp_line += ' OUT'
-                        is_matched += 1
-                # search non-terget outport
-                for w2 in targetHarness.nontarget_outport:
-                    if(w1 == w2):
-                        # this is non-terget outport
-                        # search outdex for this port
-                        index_val = targetHarness.nontarget_outport_val[targetHarness.nontarget_outport.index(w2)]
-                        tmp_line += ' WFLOAT'+str(index_val)
-                        is_matched += 1
-                # search VDD/VSS/VNW/VPW
-                if(w1.upper() == targetLib.vdd_name.upper()):
-                        tmp_line += ' '+w1.upper() 
-                        is_matched += 1
-                if(w1.upper() == targetLib.vss_name.upper()):
-                        tmp_line += ' '+w1.upper() 
-                        is_matched += 1
-                if(w1.upper() == targetLib.pwell_name.upper()):
-                        tmp_line += ' '+w1.upper() 
-                        is_matched += 1
-                if(w1.upper() == targetLib.nwell_name.upper()):
-                        tmp_line += ' '+w1.upper() 
-                        is_matched += 1
-                ## show error if this port has not matched
-                if(is_matched == 0):
-                    ## if w1 is wire name, abort
-                    ## check this is instance tmp_array[0] or circuit name tmp_array[-1]
-                    if((w1 != tmp_array[0]) and (w1 != tmp_array[-1])): 
-                        targetLib.print_error("port: "+str(w1)+" has not matched in netlist parse!!")
-                        
-            tmp_line += " "+str(tmp_array[len(tmp_array)-1])+"\n" # CIRCUIT NAME
-            outlines.append(tmp_line)
-  
-            outlines.append(".ends \n")
-            outlines.append(" \n")
-            outlines.append(cap_line)
-            outlines.append(slew_line)
-            outlines.append(cslew_line)
-            outlines.append(tunit_line)
-            outlines.append(tsetup_line)
-            outlines.append(thold_line)
-            outlines.append(tsimend_line)
-                    
-#### for ngspice batch mode 
-            outlines.append("*enable .control to show graph in ngspice \n")
-            outlines.append("*.control \n")
-            outlines.append("*run \n")
-            outlines.append("*plot V("+V_in_target+") V(VOUT) V(VCIN) \n")
-            outlines.append("*.endc \n")
-            outlines.append(".end \n") 
-            f.writelines(outlines)
-            f.close()
-  
-        spicelis = spicef
-        spicelis += ".lis"
-        spicerun = spicef
-        spicerun += ".run"
-#	#	spicelis.replace('.sp','.lis')
-  
-        # run simulation
-        if 'ngspice' in str(targetLib.simulator):
-            cmd = f'{str(targetLib.simulator)} -b {str(spicef)} 1> {str(spicelis)} 2> /dev/null \n'
-        elif 'hspice' in str(targetLib.simulator):
-            cmd = f'{str(targetLib.simulator)} {str(spicef)} -o {str(spicelis)} > /dev/null 2>&1\n'
-        #cmd = str(targetLib.simulator)+" -b "+str(spicef)+" > "+str(spicelis)+"\n"
-        with open(spicerun,'w') as f:
-            outlines = []
-            outlines.append(cmd) 
-            f.writelines(outlines)
+                else:
+                    raise ValueError(f'Invalid state for port {port}: {state}')
+        # Check clock
+        if port == harness.clock:
+            tmp_line += ' CIN'
+            is_matched += 1
+        # Check reset
+        if cell.reset and port == harness.reset:
+            tmp_line += ' RIN'
+            is_matched += 1
+        # Check set
+        if cell.set and port == harness.set:
+            tmp_line += ' SIN'
+            is_matched += 1
+        # Check target outport
+        if port == harness.target_out_port:
+            tmp_line += ' OUT'
+            is_matched += 1
+        # Check non-target outports
+        for nontarget_port, state in zip(harness.nontarget_out_ports, harness.nontarget_out_port_states):
+            if port == nontarget_port:
+                tmp_line += f' WFLOAT{str(state)}'
+                is_matched += 1
+        # Check VDD, VSS, VNW, and VPW
+        if port.upper() == settings.vdd.name.upper():
+                tmp_line += f' {port.upper()}'
+                is_matched += 1
+        if port.upper() == settings.vss.name.upper():
+                tmp_line += f' {port.upper()}'
+                is_matched += 1
+        if port.upper() == settings.pwell.name.upper():
+                tmp_line += f' {port.upper()}'
+                is_matched += 1
+        if port.upper() == settings.nwell.name.upper():
+                tmp_line += f' {port.upper()}'
+                is_matched += 1
+        ## show error if this port has not matched
+        if not is_matched:
+            raise ValueError(f"Port {port} not found in netlist")
+    tmp_line += f" {circuit_name}\n"
+    outlines.append(tmp_line)
+    outlines.append(".ends")
+
+    # Handle remaining parameters
+    outlines.extend([
+        f'.param cap = {load}{settings.units.capacitance.symbol}',
+        f'.param slew = {slew/(settings.logic_threshold_high-settings.logic_threshold_low)}{str(settings.units.time)}',
+        f'.param cslew = {cell.clock_slope}{str(settings.units.time)}',
+        f'.param tunit = {max(cell.in_slews)}{str(settings.units.time)}',
+        f'.param tsetup = {setup_time}{str(settings.units.time)}',
+        f'.param thold = {hold_time}{str(settings.units.time)}',
+        f'.param tsimendmag = {tsimendmag} tranmag = {tranmag}'
+    ])
+
+    # Write to spice file
+    with open(f'{output_filename}.sp', 'w') as f:
+        f.writelines('\n'.join(outlines))
         f.close()
-  
-        cmd = ['sh', spicerun]
-                
-        if targetLib.run_sim:
-            try:
-                res = subprocess.check_call(cmd)
-            except:
-                targetLib.print_msg("Failed to lunch spice")
 
-    # read results
-    with open(spicelis,'r') as f:
+    # Run spice simulation
+    if 'ngspice' in str(settings.simulator):
+        cmd = f'{str(settings.simulator.resolve())} -b {output_filename}.sp 1> {output_filename}.lis 2> /dev/null\n'
+    elif 'hspice' in str(settings.simulator):
+        cmd = f'{str(settings.simulator.resolve())} {output_filename}.sp -o {output_filename}.lis 2> /dev/null\n'
+    else:
+        raise ValueError(f'Unrecognized simulator "{str(settings.simulator)}"')
+    with open(f'{output_filename}.run', 'w') as f:
+        f.writelines([cmd])
+        f.close
+    if settings.run_sim:
+        subprocess.run(['sh', f'{output_filename}.run'])
+
+    # Read results from file
+    results = {}
+    desired_measurements = ['prop_in_out', 'prop_cin_out', 'trans_out', 'prop_in_d2c', 'tstart2_to_tend1']
+    if not energy:
+        desired_measurements += ['energy_start', 'energy_end', 'energy_clk_start', 'energy_clk_end']
+    else:
+        desired_measurements += ['q_in_dyn', 'q_out_dyn', 'q_clk_dyn', 'q_vdd_dyn', 'q_vss_dyn', 'i_vdd_leak', 'i_vss_leak', 'i_in_leak']
+    with open(f'{output_filename}.lis', 'r') as f:
+        # TODO: consider accumulating errors and forwarding them once all measurements are added to dictionary
         for inline in f:
-            if 'hspice' in str(targetLib.simulator):
+            if any([f in inline for f in ['failed', 'Error']]):
+                raise NameError(f'An error occurred while running simulation. See {output_filename}.lis')
+            if 'hspice' in str(settings.simulator):
                 inline = re.sub('\=',' ',inline)
-            #targetLib.print_msg(inline)
-            # search measure
-            if(not (re.search("warning*", inline)) and not (re.search("failed",inline)) and not (re.search("Error",inline))):
-                if(re.search("prop_in_out", inline)): 
-                    sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                    res_prop_in_out = "{:e}".format(float(sparray[2].strip()))
-                elif(re.search("prop_cin_out", inline)):
-                    sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                    res_prop_cin_out = "{:e}".format(float(sparray[2].strip()))
-                elif(re.search("trans_out", inline)):
-                    sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                    res_trans_out = "{:e}".format(float(sparray[2].strip()))
-                elif(re.search("prop_in_d2c", inline)):
-                    sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                    res_setup = "{:e}".format(float(sparray[2].strip()))
-                elif(re.search("prop_in_c2d", inline)):
-                    sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                    res_hold = "{:e}".format(float(sparray[2].strip()))
-                elif(re.search("tstart2_to_tend1", inline)):
-                    sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                    res_tstart2_to_tend1 = "{:e}".format(float(sparray[2].strip()))
-                if(sim_mode == "delay"):
-                    if(re.search("energy_start", inline)):
-                        sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                        res_energy_start = "{:e}".format(float(sparray[2].strip()))
-                    elif(re.search("energy_end", inline)):
-                        sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                        res_energy_end = "{:e}".format(float(sparray[2].strip()))
-                    if(re.search("energy_clk_start", inline)):
-                        sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                        res_energy_clk_start = "{:e}".format(float(sparray[2].strip()))
-                    elif(re.search("energy_clk_end", inline)):
-                        sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                        res_energy_clk_end = "{:e}".format(float(sparray[2].strip()))
-                if(sim_mode == "energy"):
-                    if(re.search("q_in_dyn", inline)):
-                        sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                        res_q_in_dyn = "{:e}".format(float(sparray[2].strip()))
-                    elif(re.search("q_out_dyn", inline)):
-                        sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                        res_q_out_dyn = "{:e}".format(float(sparray[2].strip()))
-                    elif(re.search("q_clk_dyn", inline)):
-                        sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                        res_q_clk_dyn = "{:e}".format(float(sparray[2].strip()))
-                    elif(re.search("q_vdd_dyn", inline)):
-                        sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                        res_q_vdd_dyn = "{:e}".format(float(sparray[2].strip()))
-                    elif(re.search("q_vss_dyn", inline)):
-                        sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                        res_q_vss_dyn = "{:e}".format(float(sparray[2].strip()))
-                    elif(re.search("i_vdd_leak", inline)):
-                        sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                        res_i_vdd_leak = "{:e}".format(float(sparray[2].strip()))
-                    elif(re.search("i_vss_leak", inline)):
-                        sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                        res_i_vss_leak = "{:e}".format(float(sparray[2].strip()))
-                    elif(re.search("i_in_leak", inline)):
-                        sparray = re.split(" +", inline) # separate words with spaces (use re.split)
-                        res_i_in_leak = "{:e}".format(float(sparray[2].strip()))
-
-
-    f.close()
-#	targetLib.print_msg(str(res_prop_in_out)+" "+str(res_trans_out)+" "+str(res_energy_start)+" "+str(res_energy_end)+" "+str(res_setup)+" "+str(res_hold))
-    # check spice finish successfully
-    try:
-        res_prop_in_out
-    except NameError:
-        res_prop_in_out = "failed"	
-    try:
-        res_prop_cin_out
-    except NameError:
-        res_prop_cin_out = "failed"	
-    try:
-        res_trans_out
-    except NameError:
-        res_trans_out = "failed"	
-    try:
-        res_energy_start
-    except NameError:
-        res_energy_start = "failed"	
-    try:
-        res_energy_end
-    except NameError:
-        res_energy_end = "failed"	
-    try:
-        res_energy_clk_start
-    except NameError:
-        res_energy_clk_start = "failed"	
-    try:
-        res_energy_clk_end
-    except NameError:
-        res_energy_clk_end = "failed"	
-    try:
-        res_setup
-    except NameError:
-        res_setup = "failed"	
-    try:
-        res_hold
-    except NameError:
-        res_hold = "failed"	
-    try:
-        res_q_in_dyn
-    except NameError:
-        res_q_in_dyn = "failed"	
-    try:
-        res_q_out_dyn
-    except NameError:
-        res_q_out_dyn = "failed"	
-    try:
-        res_q_clk_dyn
-    except NameError:
-        res_q_clk_dyn = "failed"	
-    try:
-        res_q_vdd_dyn
-    except NameError:
-        res_q_vdd_dyn = "failed"	
-    try:
-        res_q_vss_dyn
-    except NameError:
-        res_q_vss_dyn = "failed"	
-    try:
-        res_i_in_leak
-    except NameError:
-        res_i_in_leak = "failed"	
-    try:
-        res_i_vdd_leak
-    except NameError:
-        res_i_vdd_leak = "failed"	
-    try:
-        res_i_vss_leak
-    except NameError:
-        res_i_vss_leak = "failed"	
-    try:
-        res_tstart2_to_tend1
-    except NameError:
-        res_tstart2_to_tend1 = "failed"	
-
-    ## Overwrite res_hold if tstart2_to_tend1 is negative
-    ## this is because hspice continue spice simulation 
-    ## even if tstart2_to_tend1 is negative (illigal vin condition)
-
-#	print(str(res_tstart2_to_tend1))
-#	if(float(res_tstart2_to_tend1) < 0):
-#		print("true1")
-#	if(res_tstart2_to_tend1 == "failed"):
-#		print("true2")
-    if(res_tstart2_to_tend1 == "failed"):
-        res_prop_in_out = "failed"
-        #targetLib.print_msg_sim("Parameter res_tstart2_to_tend1 is failed, stop hold search!")
-    elif(float(res_tstart2_to_tend1) < 0):
-        res_prop_in_out = "failed"
-        #targetLib.print_msg_sim("Time points in VIN waveform is negative, stop hold search!")
+            measurement = next((m for m in desired_measurements if m in inline), False)
+            if measurement:
+                results[measurement] = float(inline.split()[2])
+        f.close()
 
-    if(sim_mode == "delay"):
-        return res_prop_in_out, res_prop_cin_out, res_trans_out, \
-                        res_energy_start, res_energy_end, res_energy_clk_start, res_energy_clk_end, \
-                        res_setup, res_hold
-                        
-    if(sim_mode == "energy"):
-        return res_prop_in_out, res_prop_cin_out, res_trans_out, \
-                        res_setup, res_hold, \
-                        res_q_in_dyn, res_q_out_dyn, res_q_clk_dyn, res_q_vdd_dyn, res_q_vss_dyn, \
-                        res_i_in_leak, res_i_vdd_leak, res_i_vss_leak 
+    return results
\ No newline at end of file
diff --git a/liberty/ExportUtils.py b/liberty/ExportUtils.py
new file mode 100644
index 0000000..5eea215
--- /dev/null
+++ b/liberty/ExportUtils.py
@@ -0,0 +1,744 @@
+
+import re, sys
+
+from liberty.LibrarySettings import LibrarySettings
+from liberty.LogicCell import LogicCell, CombinationalCell, SequentialCell
+from characterizer.Harness import CombinationalHarness
+
+def exportFiles(targetLib, targetCell):
+    if not targetLib.is_exported:
+        exportLib(targetLib, targetCell)
+    if targetLib.is_exported and not targetCell.is_exported:
+        ## export comb. logic
+        if not isinstance(targetCell, SequentialCell):
+            exportCombinationalCell(targetLib, targetCell)
+            exportVerilog(targetLib, targetCell)
+        ## export seq. logic
+        else:
+            exportSequentialCell(targetLib, targetCell)
+            exportVerilogFlop(targetLib, targetCell)
+
+## export library definition to .lib
+def exportLib(target_lib: LibrarySettings, target_cell: LogicCell):
+    """Export library definition to liberty file"""
+    outlines = []
+    ## general settings
+    outlines.append(f'library ({target_lib.lib_name}) {{\n\n')
+    outlines.append(f'  delay_model : "{target_lib.delay_model}";\n')
+    outlines.append(f'  in_place_swap_mode : match_footprint;\n')
+    outlines.append('\n')
+    outlines.append(f'  /* unit attributes */\n')
+    outlines.append(f'  time_unit : "1{str(target_lib.units.time)}";\n')
+    outlines.append(f'  voltage_unit : "1{str(target_lib.units.voltage)}";\n')
+    outlines.append(f'  current_unit : "1{str(target_lib.units.current)}";\n')
+    outlines.append(f'  pulling_resistance_unit : "1{str(target_lib.units.resistance)}";\n')
+    outlines.append(f'  leakage_power_unit : "1{str(target_lib.units.power)}";\n')
+    outlines.append(f'  capacitive_load_unit (1,{str(target_lib.units.capacitance)});\n')
+    outlines.append('\n')
+    outlines.append(f'  slew_upper_threshold_pct_rise : {str(target_lib.logic_threshold_high*100)};\n')
+    outlines.append(f'  slew_lower_threshold_pct_rise : {str(target_lib.logic_threshold_low*100)};\n')
+    outlines.append(f'  slew_upper_threshold_pct_fall : {str(target_lib.logic_threshold_high*100)};\n')
+    outlines.append(f'  slew_lower_threshold_pct_fall : {str(target_lib.logic_threshold_low*100)};\n')
+    outlines.append(f'  input_threshold_pct_rise : {str(target_lib.logic_low_to_high_threshold*100)};\n')
+    outlines.append(f'  input_threshold_pct_fall : {str(target_lib.logic_high_to_low_threshold*100)};\n')
+    outlines.append(f'  output_threshold_pct_rise : {str(target_lib.logic_low_to_high_threshold*100)};\n')
+    outlines.append(f'  output_threshold_pct_fall : {str(target_lib.logic_high_to_low_threshold*100)};\n')
+    outlines.append(f'  nom_process : 1;\n')
+    outlines.append(f'  nom_voltage : "{str(target_lib.vdd.voltage)}";\n')
+    outlines.append(f'  nom_temperature : "{str(target_lib.temperature)}";\n')
+    # outlines.append(f'  voltage_map ({str(target_lib.vdd.name)}, {str(target_lib.vdd.voltage)});\n')
+    # outlines.append(f'  voltage_map ({str(target_lib.vss.name)}, {str(target_lib.vss.voltage)});\n')
+    # outlines.append(f'  default_cell_leakage_power : 0;\n')
+    # outlines.append(f'  default_fanout_load : 1;\n')
+    # outlines.append(f'  default_max_transition : 1000;\n')
+    # outlines.append(f'  default_input_pin_cap : 0;\n')
+    # outlines.append(f'  default_inout_pin_cap : 0;\n')
+    # outlines.append(f'  default_leakage_power_density : 0;\n')
+    # outlines.append(f'  default_max_fanout : 100;\n')
+    # outlines.append(f'  default_output_pin_cap : 0;\n')
+    # outlines.append(f'  slew_derate_from_library : 1;\n')
+    ## operating conditions
+    outlines.append(f'  operating_conditions ({target_lib.operating_conditions}) {{\n')
+    outlines.append(f'    process : 1;\n')
+    outlines.append(f'    voltage : {str(target_lib.vdd.voltage)};\n')
+    outlines.append(f'    temperature : {str(target_lib.temperature)};\n')
+    outlines.append(f'  }}\n')
+    outlines.append(f'  default_operating_conditions : {target_lib.operating_conditions};\n')
+    outlines.append('\n')
+    # TODO: Generate these based on library contents (rather than the current cell)
+    outlines.append(f'  lu_table_template (constraint_template) {{\n')
+    outlines.append(f'    variable_1 : constrained_pin_transition;\n')
+    outlines.append(f'    variable_2 : related_pin_transition;\n')
+    outlines.append(f'    index_1 ("{", ".join([str(slope) for slope in target_cell.in_slews])}");\n')
+    outlines.append(f'    index_2 ("{", ".join([str(slope) for slope in target_cell.in_slews])}");\n')
+    outlines.append(f'  }}\n')
+    outlines.append(f'  lu_table_template (delay_template_{len(target_cell.in_slews)}x{len(target_cell.out_loads)}) {{\n')
+    outlines.append(f'    variable_1 : input_net_transition;\n')
+    outlines.append(f'    variable_2 : total_output_net_capacitance;\n')
+    outlines.append(f'    index_1 ("{", ".join([str(slope) for slope in target_cell.in_slews])}");\n')
+    outlines.append(f'    index_2 ("{", ".join([str(load) for load in target_cell.out_loads])}");\n')
+    outlines.append(f'  }}\n')
+    outlines.append(f'  lu_table_template (recovery_template) {{\n')
+    outlines.append(f'    variable_1 : related_pin_transition;\n')
+    outlines.append(f'    variable_2 : constrained_pin_transition;\n')
+    outlines.append(f'    index_1 ("{", ".join([str(slope) for slope in target_cell.in_slews])}");\n')
+    outlines.append(f'    index_2 ("{", ".join([str(slope) for slope in target_cell.in_slews])}");\n')
+    outlines.append(f'  }}\n')
+    outlines.append(f'  lu_table_template (removal_template) {{\n')
+    outlines.append(f'    variable_1 : related_pin_transition;\n')
+    outlines.append(f'    variable_2 : constrained_pin_transition;\n')
+    outlines.append(f'    index_1 ("{", ".join([str(slope) for slope in target_cell.in_slews])}");\n')
+    outlines.append(f'    index_2 ("{", ".join([str(slope) for slope in target_cell.in_slews])}");\n')
+    outlines.append(f'  }}\n')
+    outlines.append(f'  lu_table_template (mpw_constraint_template) {{\n')
+    outlines.append(f'    variable_1 : constrained_pin_transition;\n')
+    outlines.append(f'    index_1 ("{", ".join([str(slope) for slope in target_cell.in_slews])}");\n')
+    outlines.append(f'  }}\n')
+    outlines.append(f'  power_lut_template (passive_energy_template) {{\n')
+    outlines.append(f'    variable_1 : input_transition_time;\n')
+    outlines.append(f'    index_1 ("{", ".join([str(slope) for slope in target_cell.in_slews])}");\n')
+    outlines.append(f'  }}\n')
+    outlines.append(f'  power_lut_template (energy_template_{len(target_cell.in_slews)}x{len(target_cell.out_loads)}) {{\n')
+    outlines.append(f'    variable_1 : input_transition_time;\n')
+    outlines.append(f'    variable_2 : total_output_net_capacitance;\n')
+    outlines.append(f'    index_1 ("{", ".join([str(slope) for slope in target_cell.in_slews])}");\n')
+    outlines.append(f'    index_2 ("{", ".join([str(load) for load in target_cell.out_loads])}");\n')
+    outlines.append(f'  }}\n')
+    # outlines.append(f'  input_voltage (default_{target_lib.vdd.name}_{target_lib.vss.name}_input) {{\n')
+    # outlines.append(f'    vil : {str(target_lib.vss.voltage)};\n')
+    # outlines.append(f'    vih : {str(target_lib.vdd.voltage)};\n')
+    # outlines.append(f'    vimin : {str(target_lib.vss.voltage)};\n')
+    # outlines.append(f'    vimax : {str(target_lib.vdd.voltage)};\n')
+    # outlines.append(f'  }}\n')
+    # outlines.append(f'  output_voltage (default_{target_lib.vdd.name}_{target_lib.vss.name}_output) {{\n')
+    # outlines.append(f'    vol : {str(target_lib.vss.voltage)};\n')
+    # outlines.append(f'    voh : {str(target_lib.vdd.voltage)};\n')
+    # outlines.append(f'    vomin : {str(target_lib.vss.voltage)};\n')
+    # outlines.append(f'    vomax : {str(target_lib.vdd.voltage)};\n')
+    # outlines.append(f'  }}\n')
+    
+    with open(target_lib.dotlib_name, 'w') as f:
+        f.writelines(outlines)
+        f.close()
+    target_lib.set_exported()
+
+    ## for verilog file 
+    outlines = []
+    outlines.append(f'// Verilog model for {target_lib.lib_name};\n')
+    # TODO: Implement this
+    with open(target_lib.verilog_name, 'w') as f:
+        f.writelines(outlines)
+        f.close()
+
+## export harness data to .lib
+def exportCombinationalCell(target_lib: LibrarySettings, target_cell: CombinationalCell):
+    outlines = []
+    for line in target_cell.export(target_lib).split('\n'):
+        outlines.append(f'  {line}\n')
+
+    with open(target_lib.dotlib_name, 'a') as f:
+        f.writelines(outlines)
+        f.close()
+    target_cell.set_exported()
+
+
+## export harness data to .lib
+def exportSequentialCell(targetLib: LibrarySettings, targetCell: SequentialCell):
+    harnesses = targetCell.harnesses
+    outlines = []
+    ##
+    ## (1) clock
+    ##
+    if targetCell.clock is not None:
+        # index1 = targetCell.clock.index(target_clock) 
+        index1 = 0 
+        target_inport = targetCell.clock
+        outlines.append(f'    pin ({target_inport}) {{\n') ## clock pin start 
+        outlines.append(f'      direction : input;\n')
+        outlines.append(f'      related_power_pin : "{targetLib.vdd.name}";\n')
+        outlines.append(f'      related_ground_pin : "{targetLib.vss.name}";\n')
+        outlines.append(f'      max_transition : {str(targetCell.in_slews[-1])};\n')
+        outlines.append(f'      capacitance : "{str(targetCell.cclks[index1])}";\n')
+        outlines.append(f'      input_voltage : default_{targetLib.vdd.name}_{targetLib.vss.name}_input;\n')
+        outlines.append(f'      clock : true;\n') 
+        # internal_power() {
+        #     rise_power(passive_energy_template_7x1) {
+        #     index_1 ("0.01669, 0.030212, 0.094214, 0.230514, 0.461228, 0.919101, 2.29957");
+        #     values ("0.040923, 0.0374, 0.044187, 0.046943, 0.045139, 0.051569, 0.080466");
+        #     }
+        #     fall_power(passive_energy_template_7x1) {
+        #     index_1 ("0.016236, 0.030811, 0.094437, 0.232485, 0.461801, 0.919137, 2.29958");
+        #     values ("0.038868, 0.03793, 0.033501, 0.043008, 0.042432, 0.048973, 0.08357");
+        #     }
+        # }
+        # min_pulse_width_high : 0.145244;
+        # min_pulse_width_low : 0.226781;
+        outlines.append(f'    }}\n') ## clock pin end
+
+    ##
+    ## (2) prop/tran/setup/hold for input pins
+    ##
+    for target_inport in targetCell.in_ports:
+        for target_outport in targetCell.out_ports:
+            ## select inport with setup/hold informatioin
+            index2 = targetCell.in_ports.index(target_inport) 
+            index1 = targetCell.out_ports.index(target_outport) 
+            #print(harnessList2[index1][index2*2].timing_type_setup)
+            if((harnesses[index1][index2*2].timing_type_setup == "setup_rising") or (harnesses[index1][index2*2].timing_type_setup == "setup_falling")):
+                outlines.append(f'    pin ({target_inport}) {{\n') #### inport pin start 
+                outlines.append(f'      direction : input;\n')
+                outlines.append(f'      related_power_pin : "{targetLib.vdd.name}";\n')
+                outlines.append(f'      related_ground_pin : "{targetLib.vss.name}";\n')
+                outlines.append(f'      max_transition : {str(targetCell.in_slews[-1])};\n')
+                outlines.append(f'      capacitance : "{str(targetCell.cins[index1])}";\n')
+                outlines.append(f'      input_voltage : default_{targetLib.vdd.name}_{targetLib.vss.name}_input;\n')
+                ## (2-1) setup
+                outlines.append(f'      timing () {{\n')
+                outlines.append(f'        related_pin : "{targetCell.clock}";\n')
+                outlines.append(f'        timing_type : "{harnesses[index1][index2*2].timing_type_setup}";\n')
+                ## setup rise
+                outlines.append(f'        {harnesses[index1][index2*2].timing_sense_setup} (constraint_template) {{\n')
+                for lut_line in harnesses[index1][index2*2].lut_setup:
+                    outlines.append(f'          {lut_line}\n')
+                outlines.append(f'        }}\n') 
+                ## setup fall
+                outlines.append(f'        {harnesses[index1][index2*2+1].timing_sense_setup} (constraint_template) {{\n')
+                for lut_line in harnesses[index1][index2*2+1].lut_setup:
+                    outlines.append(f'          {lut_line}\n')
+                outlines.append(f'        }}\n') 
+                outlines.append(f'      }}\n') 
+                ## (2-2) hold
+                outlines.append(f'      timing () {{\n')
+                index1 = targetCell.out_ports.index(target_outport) 
+                outlines.append(f'        related_pin : "{targetCell.clock}";\n')
+                outlines.append(f'        timing_type : "{harnesses[index1][index2*2].timing_type_hold}";\n')
+                ## hold rise
+                outlines.append(f'        {harnesses[index1][index2*2].timing_sense_hold} (constraint_template) {{\n')
+                for lut_line in harnesses[index1][index2*2].lut_hold:
+                    outlines.append(f'          {lut_line}\n')
+                outlines.append(f'        }}\n') 
+                ## hold fall
+                outlines.append(f'        {harnesses[index1][index2*2+1].timing_sense_hold} (constraint_template) {{\n')
+                for lut_line in harnesses[index1][index2*2+1].lut_hold:
+                    outlines.append(f'          {lut_line}\n')
+                outlines.append(f'        }}\n') 
+                outlines.append(f'      }}\n') 
+                outlines.append(f'    }}\n') ## inport pin end
+
+    ##
+    ## (3) outport 
+    ##
+    for target_outport in targetCell.out_ports:
+        index1 = targetCell.out_ports.index(target_outport) 
+        outlines.append(f'    pin ({target_outport}) {{\n') #### out pin start
+        outlines.append(f'      direction : output;\n')
+        outlines.append(f'      function : "({targetCell.functions[index1]})"\n')
+        outlines.append(f'      related_power_pin : "{targetLib.vdd.name}";\n')
+        outlines.append(f'      related_ground_pin : "{targetLib.vss.name}";\n')
+        outlines.append(f'      max_capacitance : "{str(targetCell.out_loads[-1])}";\n') ## use max val. of load table
+        outlines.append(f'      output_voltage : default_{targetLib.vdd.name}_{targetLib.vss.name}_output;\n')
+        ## (3-1) clock
+        if targetCell.clock is not None:
+            ## index2 is a base pointer for harness search
+            ## index2_offset and index2_offset_max are used to 
+            ## search harness from harnessList2 which contain "timing_type_set"
+            index2 = targetCell.out_ports.index(target_outport) 
+            index2_offset = 0
+            index2_offset_max = 10
+            while(index2_offset < index2_offset_max):
+                if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_setup"):
+                    break
+                index2_offset += 1
+            if(index2_offset == 10):
+                print("Error: index2_offset exceed max. search number\n")
+                exit()
+
+            ##target_inport = targetCell.clock
+            outlines.append(f'      timing () {{\n')
+            outlines.append(f'        related_pin : "{targetCell.clock}";\n')
+            outlines.append(f'        timing_type : "{harnesses[index1][index2*2+index2_offset].timing_type_clock}";\n')
+            outlines.append(f'        timing_sense : "{harnesses[index1][index2*2+index2_offset].timing_sense_clock}";\n')
+            #### (3-1-1) rise
+            ## propagation delay
+            outlines.append(f'        {harnesses[index1][index2*2+index2_offset].direction_prop} (delay_template) {{\n')
+            for lut_line in harnesses[index1][index2*2+index2_offset].lut_prop:
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            ## transition delay
+            outlines.append(f'        {harnesses[index1][index2*2+index2_offset].direction_tran} (delay_template) {{\n')
+            for lut_line in harnesses[index1][index2*2+index2_offset].lut_tran:
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            #### (3-1-2) fall
+            ## propagation delay
+            outlines.append(f'        {harnesses[index1][index2*2+index2_offset+1].direction_prop} (delay_template) {{\n')
+            for lut_line in harnesses[index1][index2*2+index2_offset+1].lut_prop:
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            ## transition delay
+            outlines.append(f'        {harnesses[index1][index2*2+index2_offset+1].direction_tran} (delay_template) {{\n')
+            for lut_line in harnesses[index1][index2*2+index2_offset+1].lut_tran:
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            outlines.append(f'      }}\n') ## timing end 
+
+        ## (3-2) reset (one directrion)
+        if targetCell.reset is not None:
+            ##target_inport = targetCell.reset
+            outlines.append(f'      timing () {{\n')
+
+            ## Harness search for reset
+            ## index2 is an base pointer for harness search
+            ## index2_offset and index2_offset_max are used to 
+            ## search harness from harnessList2 which contain "timing_type_set"
+            index2 = targetCell.out_ports.index(target_outport) 
+            index2_offset = 0
+            index2_offset_max = 10
+            while index2_offset < index2_offset_max:
+                #print(harnessList2[index1][index2*2+index2_offset])
+                if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_reset"):
+                    break
+                index2_offset += 1
+            if index2_offset == 10:
+                print("Error: index2_offset exceed max. search number\n") #TODO: Error properly here
+                exit()
+
+            outlines.append(f'        related_pin : "{targetCell.reset}";\n')
+            outlines.append(f'        timing_sense : "{harnesses[index1][index2*2+index2_offset].timing_sense_reset}";\n')
+            outlines.append(f'        timing_type : "{harnesses[index1][index2*2+index2_offset].timing_type_reset}";\n')
+            ## (3-2-1) propagation delay
+            outlines.append(f'        {harnesses[index1][index2*2+index2_offset].direction_reset_prop} (delay_template) {{\n')
+            for lut_line in harnesses[index1][index2*2+index2_offset].lut_prop:
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            ## (3-2-2) transition delay
+            outlines.append(f'        {harnesses[index1][index2*2+index2_offset].direction_reset_tran} (delay_template) {{\n')
+            for lut_line in harnesses[index1][index2*2+index2_offset].lut_tran:
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            outlines.append(f'      }}\n') 
+
+        ## (3-3) set (one directrion)
+        if targetCell.set is not None:
+            ##target_inport = targetCell.set
+            outlines.append(f'      timing () {{\n')
+            ## Harness search for set
+            ## index2 is an base pointer for harness search
+            ## index2_offset and index2_offset_max are used to 
+            ## search harness from harnessList2 which contain "timing_type_set"
+            index2 = targetCell.out_ports.index(target_outport) 
+            index2_offset = 0
+            index2_offset_max = 10
+            while(index2_offset < index2_offset_max):
+                if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_set"):
+                    break
+                index2_offset += 1
+            if(index2_offset == 10):
+                print("Error: index2_offset exceed max. search number\n") # TODO: Error properly
+                exit()
+
+            outlines.append(f'        related_pin : "{targetCell.set}";\n')
+            outlines.append(f'        timing_sense : "{harnesses[index1][index2*2+index2_offset].timing_sense_set}";\n')
+            outlines.append(f'        timing_type : "{harnesses[index1][index2*2+index2_offset].timing_type_set}";\n')
+            ## (3-3-1) propagation delay
+            outlines.append(f'        {harnesses[index1][index2*2+index2_offset].direction_set_prop} (delay_template) {{\n')
+            for lut_line in harnesses[index1][index2*2+index2_offset].lut_prop:
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            ## (3-3-2) transition delay
+            outlines.append(f'        {harnesses[index1][index2*2+index2_offset].direction_set_tran} (delay_template) {{\n')
+            for lut_line in harnesses[index1][index2*2+index2_offset].lut_tran:
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            outlines.append(f'      }}\n') 
+        
+        ## (3-4) clock power
+        if targetCell.clock is not None:
+            ## index2 is a base pointer for harness search
+            ## index2_offset and index2_offset_max are used to 
+            ## search harness from harnessList2 which contain "timing_type_set"
+            index2 = targetCell.out_ports.index(target_outport) 
+            index2_offset = 0
+            index2_offset_max = 10
+            while index2_offset < index2_offset_max:
+                if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_setup"):
+                    break
+                index2_offset += 1
+            if index2_offset == 10:
+                print("Error: index2_offset exceed max. search number\n") # TODO: Error properly
+                exit()
+
+            outlines.append(f'      internal_power () {{\n')
+            index2 = targetCell.in_ports.index(target_inport) 
+            outlines.append(f'        related_pin : "{targetCell.clock}";\n')
+            ## rise(fall)
+            outlines.append(f'        {harnesses[index1][index2*2].direction_power} (power_template) {{\n')
+            for lut_line in harnesses[index1][index2*2].get_internal_energy_lut(targetCell.in_slews, targetCell.out_loads, targetLib.energy_meas_high_threshold_voltage(), targetLib.units.energy):
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            ## fall(rise)
+            outlines.append(f'        {harnesses[index1][index2*2+1].direction_power} (power_template) {{\n')
+            for lut_line in harnesses[index1][index2*2+1].get_internal_energy_lut(targetCell.in_slews, targetCell.out_loads, targetLib.energy_meas_high_threshold_voltage(), targetLib.units.energy):
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            outlines.append(f'      }}\n') ## power end 
+
+        ## (3-5) reset power 
+        if targetCell.reset is not None:
+
+            ## Harness search for reset
+            ## index2 is an base pointer for harness search
+            ## index2_offset and index2_offset_max are used to 
+            ## search harness from harnessList2 which contain "timing_type_set"
+            index2 = targetCell.out_ports.index(target_outport) 
+            index2_offset = 0
+            index2_offset_max = 10
+            while(index2_offset < index2_offset_max):
+                #print(harnessList2[index1][index2*2+index2_offset])
+                if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_reset"):
+                    break
+                index2_offset += 1
+            if(index2_offset == 10):
+                print("Error: index2_offset exceed max. search number\n")
+                exit()
+
+            outlines.append(f'      internal_power () {{\n')
+            index2 = targetCell.in_ports.index(target_inport) 
+            outlines.append(f'        related_pin : "{targetCell.reset}";\n')
+            ## rise(fall)
+            outlines.append(f'        {harnesses[index1][index2*2].direction_power} (power_template) {{\n')
+            for lut_line in harnesses[index1][index2*2].get_internal_energy_lut(targetCell.in_slews, targetCell.out_loads, targetLib.energy_meas_high_threshold_voltage(), targetLib.units.energy):
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            ## fall(rise)
+            outlines.append(f'        {harnesses[index1][index2*2+1].direction_power} (power_template) {{\n')
+            for lut_line in harnesses[index1][index2*2+1].get_internal_energy_lut(targetCell.in_slews, targetCell.out_loads, targetLib.energy_meas_high_threshold_voltage(), targetLib.units.energy):
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            outlines.append(f'      }}\n') ## power end 
+
+        ## (3-6) set power 
+        if targetCell.set is not None:
+
+            ## Harness search for set
+            ## index2 is an base pointer for harness search
+            ## index2_offset and index2_offset_max are used to 
+            ## search harness from harnessList2 which contain "timing_type_set"
+            index2 = targetCell.out_ports.index(target_outport) 
+            index2_offset = 0
+            index2_offset_max = 10
+            while index2_offset < index2_offset_max:
+                #print(harnessList2[index1][index2*2+index2_offset])
+                if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_set"):
+                    break
+                index2_offset += 1
+            if index2_offset == 10:
+                print("Error: index2_offset exceed max. search number\n") # TODO: Error properly
+                exit()
+
+            outlines.append(f'      internal_power () {{\n')
+            index2 = targetCell.in_ports.index(target_inport) 
+            outlines.append(f'        related_pin : "{targetCell.set}";\n')
+            ## rise(fall)
+            outlines.append(f'        {harnesses[index1][index2*2].direction_power} (power_template) {{\n')
+            for lut_line in harnesses[index1][index2*2].get_internal_energy_lut(targetCell.in_slews, targetCell.out_loads, targetLib.energy_meas_high_threshold_voltage(), targetLib.units.energy):
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            ## fall(rise)
+            outlines.append(f'        {harnesses[index1][index2*2+1].direction_power} (power_template) {{\n')
+            for lut_line in harnesses[index1][index2*2+1].get_internal_energy_lut(targetCell.in_slews, targetCell.out_loads, targetLib.energy_meas_high_threshold_voltage(), targetLib.units.energy):
+                outlines.append(f'          {lut_line}\n')
+            outlines.append(f'        }}\n') 
+            outlines.append(f'      }}\n') ## power end 
+        ##outlines.append("    }\n") #### out pin end
+    outlines.append(f'    }}\n') ## out pin end
+
+    ## (4) Reset
+    if targetCell.reset is not None:
+        # for target_reset in targetCell.reset:
+        target_reset = targetCell.reset
+        index1 = targetCell.reset.index(target_reset) 
+        outlines.append(f'    pin ({target_reset}) {{\n') #### out pin start
+        outlines.append(f'      direction : input;\n')
+        #outlines.append(f'      function : "({targetCell.functions[index1]})"\n')
+        outlines.append(f'      related_power_pin : "{targetLib.vdd.name}";\n')
+        outlines.append(f'      related_ground_pin : "{targetLib.vss.name}";\n')
+        outlines.append(f'      max_transition : {str(targetCell.in_slews[-1])};\n')
+        ##outlines.append(f'      capacitance : "{str(targetCell.crsts[index1])}";\n')
+        outlines.append(f'      capacitance : "{str(targetCell.crsts[0])}";\n') # use 0 as representative
+        outlines.append(f'      input_voltage : default_{targetLib.vdd.name}_{targetLib.vss.name}_input;\n')
+        ##target_inport = targetCell.reset
+
+        ## Harness search for reset
+        ## index2 is an base pointer for harness search
+        ## index2_offset and index2_offset_max are used to 
+        ## search harness from harnessList2 which contain "timing_type_set"
+        index2 = targetCell.out_ports.index(target_outport) 
+        index2_offset = 0
+        index2_offset_max = 10
+        while index2_offset < index2_offset_max:
+            #print(harnessList2[index1][index2*2+index2_offset])
+            if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_reset"):
+                break
+            index2_offset += 1
+        if index2_offset == 10:
+            print("Error: index2_offset exceed max. search number\n") # TODO: Error properly
+            exit()
+        #for a in inspect.getmembers(harnessList2[index1][index2*2+index2_offset]):
+        #	print (a) 
+        ## (4-1) recovery
+        outlines.append(f'      timing () {{\n')
+        outlines.append(f'        related_pin : "{targetCell.clock}";\n')
+        outlines.append(f'        timing_type : "{harnesses[index1][index2*2+index2_offset].timing_type_reset_recov}";\n')
+        if (1 != len(targetCell.in_ports)):
+            print (f'Error: number of targetCell.in_ports is {str(len(targetCell.in_ports))}, not one!') # TODO: Error properly
+            exit()
+        #for target_inport in targetCell.in_ports: ## only one target_inport should be available
+        #	outlines.append("        when : \""+harnessList2[index1][index2*2+index2_offset].timing_type_reset+"\";\n")
+        outlines.append(f'        {harnesses[index1][index2*2+index2_offset].timing_sense_reset_recov} (recovery_template) {{\n')
+        for lut_line in harnesses[index1][index2*2+index2_offset].lut_setup:
+            outlines.append(f'          {lut_line}\n')
+        outlines.append(f'        }}\n') 
+        outlines.append(f'      }}\n') 
+        ## (4-2)removal 
+        outlines.append(f'      timing () {{\n')
+        outlines.append(f'        related_pin : "{targetCell.clock}";\n')
+        outlines.append(f'        timing_type : "{harnesses[index1][index2*2+index2_offset].timing_type_reset_remov}";\n')
+        if (1 != len(targetCell.in_ports)):
+            print (f'Error: number of targetCell.in_ports is {str(len(targetCell.in_ports))}, not one!') # TODO: Error properly
+            exit()
+        #for target_inport in targetCell.in_ports: ## only one target_inport should be available
+        #	outlines.append("        when : \""+harnessList2[index1][index2*2+index2_offset].timing_type_reset+"\";\n")
+        outlines.append(f'        {harnesses[index1][index2*2+index2_offset].timing_sense_reset_remov} (removal_template) {{\n')
+        for lut_line in harnesses[index1][index2*2+index2_offset].lut_hold:
+            outlines.append(f'          {lut_line}\n')
+        outlines.append(f'        }}\n') 
+        outlines.append(f'      }}\n') 
+    outlines.append(f'    }}\n') #### out pin end -> continue for set
+
+    ## (5) set
+    if targetCell.set is not None:
+        #for target_set in targetCell.set:
+        target_set = targetCell.set
+        index1 = targetCell.set.index(target_set) 
+        outlines.append(f'    pin ({target_set}) {{\n') #### out pin start
+        outlines.append(f'      direction : input;\n')
+        #outlines.append(f'      function : "({targetCell.functions[index1]})"\n')
+        outlines.append(f'      related_power_pin : "{targetLib.vdd.name}";\n')
+        outlines.append(f'      related_ground_pin : "{targetLib.vss.name}";\n')
+        outlines.append(f'      max_transition : {str(targetCell.in_slews[-1])};\n')
+        #outlines.append(f'      capacitance : "{str(targetCell.csets[index1])}";\n')
+        outlines.append(f'      capacitance : "{str(targetCell.csets[0])}";\n') # use 0 as representative val
+        outlines.append(f'      input_voltage : default_{targetLib.vdd.name}_{targetLib.vss.name}_input;\n')
+        ##target_inport = targetCell.set
+
+        ## Harness search for set
+        ## index2 is an base pointer for harness search
+        ## index2_offset and index2_offset_max are used to 
+        ## search harness from harnessList2 which contain "timing_type_set"
+        index2 = targetCell.out_ports.index(target_outport) 
+        index2_offset = 0
+        index2_offset_max = 10
+        while index2_offset < index2_offset_max:
+            #print(harnessList2[index1][index2*2+index2_offset])
+            if hasattr(harnesses[index1][index2*2+index2_offset], "timing_type_set"):
+                break
+            index2_offset += 1
+        if index2_offset == 10:
+            print("Error: index2_offset exceed max. search number\n") # TODO: Error properly
+            exit()
+
+        ## (5-1) recovery
+        outlines.append(f'      timing () {{\n')
+        outlines.append(f'        related_pin : "{targetCell.clock}";\n')
+        outlines.append(f'        timing_type : "{harnesses[index1][index2*2+index2_offset].timing_type_set_recov}";\n')
+        if (1 != len(targetCell.in_ports)):
+            print (f'Error: number of targetCell.in_ports is {str(len(targetCell.in_ports))}, not one!') # TODO: Error properly
+            exit()
+        #for target_inport in targetCell.in_ports: ## only one target_inport should be available
+        #	outlines.append("        when : \""+harnessList2[index1][index2*2+index2_offset].timing_type_set+"\";\n")
+        outlines.append(f'        {harnesses[index1][index2*2+index2_offset].timing_sense_set_recov} (recovery_template) {{\n')
+        for lut_line in harnesses[index1][index2*2+index2_offset].lut_setup:
+            outlines.append(f'          {lut_line}\n')
+        outlines.append(f'        }}\n')
+        outlines.append(f'      }}\n')
+
+        ## (5-2)removal 
+        outlines.append(f'      timing () {{\n')
+        outlines.append(f'        related_pin : "{targetCell.clock}";\n')
+        outlines.append(f'        timing_type : "{harnesses[index1][index2*2+index2_offset].timing_type_set_remov}";\n')
+        if (1 != len(targetCell.in_ports)):
+            print (f'Error: number of targetCell.in_ports is {str(len(targetCell.in_ports))}, not one!') # TODO: Error properly
+            exit()
+        #for target_inport in targetCell.in_ports: ## only one target_inport should be available
+        #	outlines.append("        when : \""+harnessList2[index1][index2*2+index2_offset].timing_type_set+"\";\n")
+        outlines.append(f'        {harnesses[index1][index2*2+index2_offset].timing_sense_set_remov} (removal_template) {{\n')
+        for lut_line in harnesses[index1][index2*2+index2_offset].lut_hold:
+            outlines.append(f'          {lut_line}\n')
+        outlines.append(f'        }}\n')
+        outlines.append(f'      }}\n')
+    outlines.append(f'    }}\n') #### out pin end -> continue for set
+
+    outlines.append(f'  }}\n') #### cell end
+    with open(targetLib.dotlib_name, 'a') as f:
+        f.writelines(outlines)
+        f.close()
+    targetCell.set_exported()
+
+
+## export library definition to .lib
+def exportVerilog(targetLib, targetCell):
+    outlines = []
+
+    ## list ports in one line 
+    portlist = "("
+    numport = 0
+    for target_outport in targetCell.out_ports:
+        if numport != 0:
+            portlist += ", "
+        portlist += target_outport
+        numport += 1
+    for target_inport in targetCell.in_ports:
+        portlist += "," + target_inport
+        numport += 1
+    portlist += ");"
+
+    outlines.append(f'module {targetCell.name + portlist}\n')
+
+    ## input/output statement
+    for target_outport in targetCell.out_ports:
+        outlines.append(f'output {target_outport};\n')
+    for target_inport in targetCell.in_ports:
+        outlines.append(f'input {target_inport};\n')
+
+    ## branch for combinational cell
+    else:
+    ## assign statement
+        for target_outport in targetCell.out_ports:
+            port_index = targetCell.out_ports.index(target_outport) 
+            outlines.append(f'assign {target_outport} = {targetCell.functions[port_index]};\n')
+
+    outlines.append("endmodule\n\n")
+    
+    with open(targetLib.verilog_name, 'a') as f:
+        f.writelines(outlines)
+        f.close()
+
+
+## export library definition to .lib
+def exportVerilogFlop(targetLib, targetCell):
+
+    outlines = []
+
+    ## list ports in one line 
+    portlist = "("
+    numport = 0
+    for target_outport in targetCell.out_ports:
+        if numport != 0:
+            portlist += ", "
+        portlist += target_outport
+        numport += 1
+    for target_inport in targetCell.in_ports:
+        portlist += "," + target_inport
+        numport += 1
+    if targetCell.clock is not None:
+        portlist += "," + targetCell.clock
+        numport += 1
+    if targetCell.reset is not None:
+        portlist += "," + targetCell.reset
+        numport += 1
+    if targetCell.set is not None:
+        portlist += "," + targetCell.set
+        numport += 1
+    portlist += ");"
+
+    outlines.append(f'module {targetCell.name + portlist}\n')
+
+    ## input/output statement
+    for target_outport in targetCell.out_ports:
+        outlines.append(f'output {target_outport};\n')
+    for target_inport in targetCell.in_ports:
+        outlines.append(f'input {target_inport};\n')
+    if targetCell.clock is not None:
+        outlines.append(f'input {targetCell.clock};\n')
+    if targetCell.reset is not None:
+        outlines.append(f'input {targetCell.reset};\n')
+    if targetCell.set is not None:
+        outlines.append(f'input {targetCell.set};\n')
+
+    ## assign statement
+    for target_outport in targetCell.out_ports:
+        for target_inport in targetCell.in_ports:
+            line = 'always @('
+            resetlines = []
+            setlines = []
+            ## clock
+            # TODO: replace
+            # if 'PC' in targetCell.logic:    ## posedge clock
+            #     line += "posedge " + targetCell.clock
+            # elif 'NC' in targetCell.logic:  ## negedge clock
+            #     line += "negedge " + targetCell.clock
+            # else:
+            #     print("Error! failed to generate DFF verilog!") # TODO: Error properly
+            #     exit()
+
+            ## reset (option)
+            if targetCell.reset is not None:    ## reset
+                if 'PR' in targetCell.reset:    ## posedge async. reset
+                    line += " or posedge " + targetCell.reset
+                    resetlines.append(f'if ({targetCell.reset}) \n')
+                    resetlines.append(f'  {target_outport}<=0;\n')
+                    resetlines.append(f'else begin\n')
+                elif 'NR' in targetCell.reset:  ## negedge async. reset
+                    line += " or negedge " + targetCell.reset
+                    resetlines.append(f'if (!{targetCell.reset}) \n')
+                    resetlines.append(f'  {target_outport}<=0;\n')
+                    resetlines.append(f'else begin\n')
+            ## set (option)
+            if targetCell.set is not None: ## reset
+                if 'PS' in targetCell.set:  ## posedge async. set
+                    line += " or posedge " + targetCell.set
+                    setlines.append(f'if ({targetCell.set}) begin\n')
+                    setlines.append(f'  {target_outport}<=1;\n')
+                    setlines.append(f'end\n')
+                    setlines.append(f'else begin\n')
+                elif 'NS' in targetCell.set:    ## negedge async. set
+                    line += " or negedge " + targetCell.set
+                    setlines.append(f'if (!{targetCell.set}) begin\n')
+                    setlines.append(f'  {target_outport}<=1;\n')
+                    setlines.append(f'end\n')
+                    setlines.append(f'else begin\n')
+            line += ") begin\n"
+            outlines.append(line)
+            if targetCell.set is not None:
+                outlines.append(setlines[0])
+                outlines.append(setlines[1])
+                outlines.append(setlines[2])
+            if targetCell.reset is not None:
+                outlines.append(resetlines[0])
+                outlines.append(resetlines[1])
+                outlines.append(resetlines[2])
+            outlines.append(target_outport+'<='+target_inport)
+            outlines.append('\nend\n')
+            outlines.append('end\n')
+        ## for target_inport
+    ## for target_outport
+    outlines.append("endmodule\n\n")
+
+    with open(targetLib.verilog_name, 'a') as f:
+        f.writelines(outlines)
+        f.close()
+
+
+## export harness data to .lib
+def exitFiles(targetLib, num_gen_file):
+    if targetLib.is_exported:
+        outlines = []
+        outlines.append("}\n")
+        with open(targetLib.dotlib_name, 'a') as f:
+            f.writelines(outlines)
+            f.close()
+        print("\n-- .lib file exported")
+    else:
+        print("\n-- Nothing to export")
+    print("-- "+str(num_gen_file)+" cells generated\n")
diff --git a/characterizer/LibrarySettings.py b/liberty/LibrarySettings.py
similarity index 63%
rename from characterizer/LibrarySettings.py
rename to liberty/LibrarySettings.py
index bca1fc1..ad03dac 100644
--- a/characterizer/LibrarySettings.py
+++ b/liberty/LibrarySettings.py
@@ -1,26 +1,17 @@
 from pathlib import Path
-from characterizer.UnitsSettings import UnitsSettings
+from shutil import which
+from liberty.UnitsSettings import UnitsSettings
 
 class NamedNode:
-    def __init__(self, name = None, voltage = None):
-        self._name = name
-        self._voltage = voltage
+    def __init__(self, name, voltage = 0):
+        self.name = name
+        self.voltage = voltage
 
-    @property
-    def name(self) -> str:
-        return self._name
-    
-    @name.setter
-    def name(self, name: str):
-        self._name = name
+    def __str__(self) -> str:
+        return f'Name: {self.name}\nVoltage: {self.voltage}'
 
-    @property
-    def voltage(self) -> float:
-        return self._voltage
-    
-    @voltage.setter
-    def voltage(self, voltage: float):
-        self._voltage = voltage
+    def __repr__(self) -> str:
+        return f'NamedNode({self.name}, {self.voltage})'
 
     def normalized_voltage(self, units_settings: UnitsSettings) -> float:
         """Returns the voltage after converting from global units to V"""
@@ -28,51 +19,99 @@ def normalized_voltage(self, units_settings: UnitsSettings) -> float:
 
 
 def str_to_bool(value: str) -> bool:
-    if value.lower() in ['true', 't', '1']:
+    if value.lower() in ['true', 't', '1', 'yes']:
         return True
-    elif value.lower() in ['false', 'f', '0']:
+    elif value.lower() in ['false', 'f', '0', 'no']:
         return False
     else:
         raise ValueError(f'Unable to convert "{value}" to bool.')
 
 
 class LibrarySettings:
-    def __init__(self):
-        # Simulator Configuration
-        self._work_dir = Path('work')
-        self._simulator = Path('/usr/bin/ngspice')
-
+    def __init__(self, **kwargs):
         # Key Library settings
-        self._lib_name = None
-        self._dotlib_name = None
-        self._verilog_name = None
-        self._process = None
-        self._temperature = None
-        self._cell_name_suffix = None
-        self._cell_name_prefix = None
-        self.units = UnitsSettings()
-        self.vdd = NamedNode('VDD')
-        self.vss = NamedNode('VSS')
-        self.pwell = NamedNode('VPW')
-        self.nwell = NamedNode('VNW')
-        self._logic_threshold_low = 0.2
-        self._logic_threshold_high = 0.8
-        self._logic_high_to_low_threshold = 0.5
-        self._logic_low_to_high_threshold = 0.5
-        self._energy_meas_low_threshold = 0.01
-        self._energy_meas_high_threshold = 0.99
-        self._energy_meas_time_extent = 10
-        self._operating_conditions = None
-        self._delay_model = "table_lookup"
-        self._run_sim = True
-        self._mt_sim = True
-
-        # Behavioral settings
-        self._is_exported = False # whether the library settings have been exported
+        self._lib_name = kwargs.get('lib_name', 'unnamed_lib')
+        self._dotlib_name = kwargs.get('dotlib_name')
+        self._verilog_name = kwargs.get('verilog_name')
+        self._cell_name_suffix = kwargs.get('cell_name_suffix', '')
+        self._cell_name_prefix = kwargs.get('cell_name_prefix', '')
+        self.units = UnitsSettings(**kwargs.get('units', {}))
+
+        # Simulator Settings
+        self._simulator = Path(kwargs.get('simulator', which('ngspice')))
+        self._work_dir = Path(kwargs.get('work_dir', 'work'))
+        self._results_dir = Path(kwargs.get('results_dir', 'results'))
+        self._run_sim = kwargs.get('run_simulation', True)
+        self._use_multithreaded = kwargs.get('multithreaded', True)
+        self._is_exported = False
+
+        named_nodes = kwargs.get('named_nodes', {})
+        self.vdd = NamedNode(**named_nodes.get('vdd', {'name':'VDD'}))
+        self.vss = NamedNode(**named_nodes.get('vss', {'name':'VSS'}))
+        self.pwell = NamedNode(**named_nodes.get('pwell', {'name':'VPW'}))
+        self.nwell = NamedNode(**named_nodes.get('nwell', {'name':'VNW'}))
+
+        logic_thresholds = kwargs.get('logic_thresholds', {})
+        self._logic_threshold_low = logic_thresholds.get('low', 0.2)
+        self._logic_threshold_high = logic_thresholds.get('high', 0.8)
+        self._logic_high_to_low_threshold = logic_thresholds.get('high_to_low', 0.5)
+        self._logic_low_to_high_threshold = logic_thresholds.get('low_to_high', 0.5)
+
+        energy_measurement = kwargs.get('energy_measurement', {})
+        self._energy_meas_low_threshold = energy_measurement.get('low_threshold', 0.01)
+        self._energy_meas_high_threshold = energy_measurement.get('high_threshold', 0.99)
+        self._energy_meas_time_extent = energy_measurement.get('time_extent', 10)
+
+        self._process = kwargs.get('process')
+        self._temperature = kwargs.get('temperature', 25)
+        self._operating_conditions = kwargs.get('operating_conditions')
+        self._delay_model = kwargs.get('delay_model', 'table_lookup')
+        self.cell_defaults = kwargs.get('cell_defaults', {})
+
+        # TODO: Deprecate these settings
         self._suppress_msg = False
         self._suppress_sim_msg = False
         self._suppress_debug_msg = False
 
+    def __str__(self) -> str:
+        lines = []
+        lines.append(f'Library name:         {self.lib_name}')
+        lines.append(f'.lib name:            {self.dotlib_name}')
+        lines.append(f'.v name:              {self.verilog_name}')
+        lines.append(f'Cell suffix:          {self.cell_name_suffix}')
+        lines.append(f'Cell prefix:          {self.cell_name_prefix}')
+        lines.append(f'Units: ')
+        for line in str(self.units).split('\n'):
+            lines.append(f'    {line}')
+        lines.append(f'Simulator:            {str(self.simulator)}')
+        lines.append(f'Work directory:       {str(self.work_dir)}')
+        lines.append(f'Process:              {self.process}')
+        lines.append(f'Temperature:          {str(self.temperature)}')
+        lines.append(f'vdd:')
+        for line in str(self.vdd).split('\n'):
+            line = line if not 'Voltage: ' in line else f'{line} {str(self.units.voltage)}'
+            lines.append(line)
+        for line in str(self.vss).split('\n'):
+            line = line if not 'Voltage: ' in line else f'{line} {str(self.units.voltage)}'
+            lines.append(line)
+        for line in str(self.pwell).split('\n'):
+            line = line if not 'Voltage: ' in line else f'{line} {str(self.units.voltage)}'
+            lines.append(line)
+        for line in str(self.nwell).split('\n'):
+            line = line if not 'Voltage: ' in line else f'{line} {str(self.units.voltage)}'
+            lines.append(line)
+        lines.append(f'Logic thresholds:')
+        lines.append(f'    Low:              {str(self.logic_threshold_low_voltage())} {str(self.units.voltage)}')
+        lines.append(f'    High:             {str(self.logic_threshold_high_voltage())} {str(self.units.voltage)}')
+        lines.append(f'    High to low:      {str(self.logic_high_to_low_threshold_voltage())} {str(self.units.voltage)}')
+        lines.append(f'    Low to high:      {str(self.logic_low_to_high_threshold_voltage())} {str(self.units.voltage)}')
+        lines.append(f'Energy measurement thresholds:')
+        lines.append(f'    Low:              {str(self.energy_meas_low_threshold_voltage())} {str(self.units.voltage)}')
+        lines.append(f'    High:             {str(self.energy_meas_high_threshold_voltage())} {str(self.units.voltage)}')
+        lines.append(f'Operating conditions: {self.operating_conditions}')
+        lines.append(f'Delay model:          {self.delay_model}')
+        return '\n'.join(lines)
+
     @property
     def work_dir(self) -> Path:
         return self._work_dir
@@ -130,7 +169,9 @@ def dotlib_name(self) -> str:
     @dotlib_name.setter
     def dotlib_name(self, value: str):
         if value is not None and len(value) > 0:
-            self._dotlib_name = value
+            if not str(value).endswith('.lib'):
+                raise ValueError(f'Dotlib name must end in .lib!')
+            self._dotlib_name = str(value)
         else:
             raise ValueError(f'Invalid value for dotlib_name: {value}')
 
@@ -144,7 +185,9 @@ def verilog_name(self) -> str:
     @verilog_name.setter
     def verilog_name(self, value: str):
         if value is not None and len(value) > 0:
-            self._verilog_name = value
+            if not str(value).endswith('.v'):
+                raise ValueError(f'Verilog name must end in .v!')
+            self._verilog_name = str(value)
         else:
             raise ValueError(f'Invalid value for verilog_name: {value}')
 
@@ -326,20 +369,20 @@ def run_sim(self, value):
             raise ValueError(f'Invalid value for run_sim: {value}')
 
     @property
-    def mt_sim(self) -> bool:
-        return self._mt_sim
+    def use_multithreaded(self) -> bool:
+        return self._use_multithreaded
 
-    @mt_sim.setter
-    def mt_sim(self, value):
+    @use_multithreaded.setter
+    def use_multithreaded(self, value):
         if value is not None:
             if isinstance(value, str):
-                self._mt_sim = str_to_bool(value)
+                self._use_multithreaded = str_to_bool(value)
             elif isinstance(value, bool):
-                self._mt_sim = value
+                self._use_multithreaded = value
             else:
-                raise TypeError(f'Invalid type for mt_sim: {type(value)}')
+                raise TypeError(f'Invalid type for use_multithreaded: {type(value)}')
         else:
-            raise ValueError(f'Invalid value for mt_sim: {value}')
+            raise ValueError(f'Invalid value for use_multithreaded: {value}')
     
     @property
     def is_exported(self) -> bool:
@@ -371,15 +414,3 @@ def suppress_debug_message(self) -> bool:
     @suppress_debug_message.setter
     def suppress_debug_message(self, value: str):
         self._suppress_debug_msg = str_to_bool(value)
-
-    def print_msg(self, message=""):
-        if not self.suppress_message:
-            print(message)
-    
-    def print_msg_sim(self, message=""):
-        if not self.suppress_sim_message:
-            print(message)
-    
-    def print_msg_dbg(self,  message=""):
-        if not self.suppress_debug_message:
-            print(message)
diff --git a/liberty/LogicCell.py b/liberty/LogicCell.py
new file mode 100644
index 0000000..824011e
--- /dev/null
+++ b/liberty/LogicCell.py
@@ -0,0 +1,825 @@
+import threading
+from pathlib import Path
+
+import characterizer.char_comb
+import characterizer.char_seq
+from characterizer.Harness import CombinationalHarness, SequentialHarness, filter_harnesses_by_ports, find_harness_by_arc, check_timing_sense
+from characterizer.LogicParser import parse_logic
+
+class LogicCell:
+    def __init__ (self, name: str, in_ports: list, out_ports: list, functions: str, **kwargs):
+        self.name = name            # cell name
+        self.in_ports = in_ports    # input pin names
+        self.out_ports = out_ports  # output pin names
+        self.functions = functions  # cell functions
+
+        # Documentation
+        self.area = kwargs.get('area', 0) # cell area
+
+        # Characterization settings
+        self.harnesses = [] # list of lists of harnesses indexed by in_slews and out_loads
+        self._netlist = kwargs.get('netlist')               # Cell spice netlist
+        self._model = kwargs.get('model')                   # cell transistor models
+        self._in_slews = kwargs.get('slews', [])            # input pin slew rates
+        self._out_loads = kwargs.get('loads', [])           # output pin capacitive loads
+        self._sim_timestep = 0
+        if 'simulation_timestep' in kwargs.keys():
+            self.sim_timestep = kwargs['simulation_timestep']
+        self.stored_test_vectors = kwargs.get('test_vectors')
+
+        # Behavioral settings
+        self._is_exported = False   # whether the cell has been exported
+
+    def __str__(self) -> str:
+        lines = []
+        lines.append(self.name)
+        lines.append(f'    Inputs:              {", ".join(self.in_ports)}')
+        lines.append(f'    Outputs:             {", ".join(self.out_ports)}')
+        lines.append(f'    Functions:')
+        for p,f in zip(self.out_ports,self.functions):
+            lines.append(f'    {p}={f}')
+        if self.area:
+            lines.append(f'    Area:                {str(self.area)}')
+        if self.netlist:
+            lines.append(f'    Netlist:             {str(self.netlist)}')
+            lines.append(f'    Definition:          {self.definition.rstrip()}')
+            lines.append(f'    Instance:            {self.instance}')
+        if self.in_slews:
+            lines.append(f'    Input pin simulation slopes:')
+            for slope in self.in_slews:
+                lines.append(f'        {str(slope)}')
+        if self.out_loads:
+            lines.append(f'    Output pin simulation loads:')
+            for load in self.out_loads:
+                lines.append(f'        {str(load)}')
+        lines.append(f'    Simulation timestep: {str(self.sim_timestep)}')
+        if self.harnesses:
+            lines.append(f'    Harnesses:')
+            for harness in self.harnesses:
+                [lines.append(f'        {h}') for h in str(harness).split('\n')]
+        return '\n'.join(lines)
+
+    def __repr__(self):
+        return f'LogicCell({self.name},{self.in_ports},{self.out_ports},{self.functions},{self.area})'
+
+    @property
+    def name(self) -> str:
+        return self._name
+
+    @name.setter
+    def name(self, value: str):
+        if isinstance(value, str):
+            if len(value) > 0:
+                self._name = str(value)
+            else:
+                raise ValueError(f'Empty string not allowed for cell name')
+        else:
+            raise TypeError(f'Invalid type for cell name: {type(value)}')
+
+    @property
+    def in_ports(self) -> list:
+        return self._in_ports
+
+    @in_ports.setter
+    def in_ports(self, value):
+        if isinstance(value, str):
+            # Should be in the format "A B C"
+            # TODO: add parsing for comma separated as well
+            self._in_ports = value.split()
+        elif isinstance(value, list):
+            self._in_ports = value
+        else:
+            raise TypeError(f'Invalid type for in_ports: {type(value)}')
+
+    @property
+    def out_ports(self) -> list:
+        return self._out_ports
+
+    @out_ports.setter
+    def out_ports(self, value):
+        if isinstance(value, str):
+            # Should be in the format "Y Z"
+            # TODO: add parsing for comma separated as well
+            self._out_ports = value.split()
+        elif isinstance(value, list):
+            self._out_ports = value
+        else:
+            raise TypeError(f'Invalid type for out_ports: {type(value)}')
+
+    @property
+    def functions(self) -> list:
+        return self._functions
+
+    @functions.setter
+    def functions(self, value):
+        if isinstance(value, list):
+            # Should be in the format ['Y=expr1', 'Z=expr2']
+            expressions = []
+            for f in value:
+                if '=' in f:
+                    expr = f.split('=')[1:] # Discard LHS of equation
+                    if parse_logic(''.join(expr)): # Make sure the expression is verilog
+                        expressions.extend(expr)
+                else:
+                    raise ValueError(f'Expected an expression of the form "Y=A Z=B" for cell function, got "{value}"')
+            self._functions = expressions
+        elif isinstance(value, str) and '=' in value:
+            self.functions = value.split() # Split on space and recurse to use the list setter
+        else:
+            raise TypeError(f'Invalid type for cell functions: {type(value)}')
+
+    @property
+    def area(self) -> float:
+        return self._area
+
+    @area.setter
+    def area(self, value: float):
+        if value is not None:
+            self._area = float(value)
+        else:
+            raise TypeError(f'Invalid type for cell area: {type}')
+
+    @property
+    def model(self):
+        return self._model
+    
+    @model.setter
+    def model(self, value):
+        if isinstance(value, Path):
+            if not value.is_file():
+                raise ValueError(f'Invalid value for model: {value} is not a file')
+            else:
+                self._model = value
+        elif isinstance(value, str):
+            if not Path(value).is_file():
+                raise ValueError(f'Invalid value for model: {value} is not a file')
+            else:
+                self._model = value
+        else:
+            raise TypeError(f'Invalid type for model: {type(value)}')
+
+    @property
+    def netlist(self) -> str:
+        return self._netlist
+
+    @netlist.setter
+    def netlist(self, value):
+        if isinstance(value, Path):
+            if not value.is_file():
+                raise ValueError(f'Invalid value for netlist: {value} is not a file')
+            self._netlist = value
+        elif isinstance(value, str):
+            if not Path(value).is_file():
+                raise ValueError(f'Invalid value for netlist: {value} is not a file')
+            self._netlist = Path(value)
+        else:
+            raise TypeError(f'Invalid type for netlist: {type(value)}')
+
+    @property
+    def definition(self) -> str:
+        with open(self.netlist, 'r') as f:
+            for line in f:
+                if self.name.lower() in line.lower() and 'subckt' in line.lower():
+                    f.close()
+                    return line
+            # If we reach this line before returning, the netlist file doesn't contain a circuit definition
+            f.close()
+            raise ValueError(f'No cell definition found in netlist {self.netlist}')
+
+    @property
+    def instance(self) -> str:
+        instance = self.definition.split()[1:]  # Delete .subckt
+        instance.append(instance.pop(0))    # Move circuit name to last element
+        instance.insert(0, 'XDUT')              # Insert instance name
+        return ' '.join(instance)
+
+    @property
+    def in_slews(self) -> list:
+        return self._in_slews
+
+    def add_in_slew(self, value: float):
+        self._in_slews.append(float(value))
+
+    @property
+    def out_loads(self) -> list:
+        return self._out_loads
+
+    def add_out_load(self, value: float):
+        self._out_loads.append(float(value))
+
+    @property
+    def is_exported(self) -> bool:
+        return self._is_exported
+
+    def set_exported(self):
+        self._is_exported = True
+
+    @property
+    def sim_timestep(self):
+        return self._sim_timestep
+
+    @sim_timestep.setter
+    def sim_timestep(self, value):
+        if value == 'auto':
+            if self.in_slews:
+                # Use 1/10th of minimum slew rate
+                self._sim_timestep = min(self.in_slews)/10.0
+            else:
+                raise ValueError('Cannot use auto for sim_timestep unless in_slews is set first!')
+        elif isinstance(value, (float, int, str)):
+            self._sim_timestep = float(value)
+        else:
+            raise TypeError(f'Invalid type for sim_timestep: {type(value)}')
+    
+    def _gen_graycode(self, n: int):
+        """Generate the list of Gray Codes for length n"""
+        if n <= 1:
+            return [[0],[1]]
+        inputs = []
+        for j in self._gen_graycode(n-1):
+            j.insert(0, 0)
+            inputs.append(j)
+        for j in reversed(self._gen_graycode(n-1)):
+            j.insert(0, 1)
+            inputs.append(j)
+        return inputs
+
+    @property
+    def test_vectors(self) -> list:
+        """Generate a list of test vectors from this cell's functions"""
+        # TODO: Determine whether this will actually work for sequential cells, or only combinational cells
+        # 
+        # Note that this uses "brute force" methods to determine test vectors. It also tests far
+        # more vectors than necessary, lengthening simulation times.
+        # A smarter approach would be to parse the function for each output, determine potential
+        # critical paths, and then test only those paths to determine worst-case delays.
+        # 
+        # Revisiting this: we actually can't know critical path just from the function, as the
+        # given function may not match up with hardware implementation in terms of operation order.
+        # We have to evaluate all potential masking conditions and determine critical paths
+        # afterwards.
+        # 
+        # Revisiting this again: While we can't know critical paths from the information given, we
+        # could provide tools to let users tell us which conditions reveal the critical path.
+        # Consider adding an option to let users provide critical path nonmasking conditions.
+        if self.stored_test_vectors:
+            return self.stored_test_vectors
+        else:
+            test_vectors = []
+            values = self._gen_graycode(len(self.in_ports))
+            for out_index in range(len(self.out_ports)):
+                # Assemble a callable function corresponding to this output port's function
+                f = eval(f'lambda {",".join(self.in_ports)} : int({self.functions[out_index].replace("~", "not ")})')
+                for j in range(len(values)):
+                    # Evaluate f at the last two values and see if the output changes
+                    x0 = values[j-1]
+                    x1 = values[j]
+                    y0 = f(*x0)
+                    y1 = f(*x1)
+                    if not y1 == y0:
+                        # If the output differs, we can use these two vectors to test the input at the index where they differ
+                        in_index = [k for k in range(len(x1)) if x0[k] != x1[k]][0] # If there is more than 1 element here, we have a problem with our gray coding
+                        # Add two test vectors: one for rising and one for falling
+                        # Generate the first test vector
+                        test_vector = [str(e) for e in x1]
+                        test_vector[in_index] = f'{x0[in_index]}{x1[in_index]}'
+                        for n in range(len(self.out_ports)):
+                            test_vector.append(f'{y0}{y1}' if n == out_index else '0')
+                        test_vectors.append(test_vector)
+                        # Generate the second test vector
+                        test_vector = [str(e) for e in x0]
+                        test_vector[in_index] = f'{x1[in_index]}{x0[in_index]}'
+                        for n in range(len(self.out_ports)):
+                            test_vector.append(f'{y1}{y0}' if n == out_index else '0')
+                        test_vectors.append(test_vector)
+            [print(t) for t in test_vectors]
+            return test_vectors
+
+    def get_input_capacitance(self, in_port, vdd_voltage, capacitance_unit):
+        """Average input capacitance measured by all harnesses that target this input port"""
+        if in_port not in self.in_ports:
+            raise ValueError(f'Unrecognized input port {in_port}')
+        input_capacitance = 0
+        n = 0
+        for harness in self.harnesses:
+            if harness.target_in_port == in_port:
+                input_capacitance += harness.average_input_capacitance(vdd_voltage, capacitance_unit)
+                n += 1
+        return input_capacitance / n
+
+class CombinationalCell(LogicCell):
+    def __init__(self, name: str, in_ports: list, out_ports: list, functions: str, **kwargs):
+        super().__init__(name, in_ports, out_ports, functions, **kwargs)
+
+    def characterize(self, settings):
+        """Run delay characterization for an N-input M-output combinational cell"""
+        # Run delay simulation for all test vectors
+        unsorted_harnesses = []
+        for test_vector in self.test_vectors:
+            # Generate harness
+            harness = CombinationalHarness(self, test_vector)
+            # Determine spice filename prefix
+            spice_prefix = f'{settings.work_dir}/delay_{self.name}_{harness.spice_infix()}'
+            # Run delay characterization
+            if settings.use_multithreaded:
+                # Split simulation jobs into threads and run multiple simultaneously
+                thread_id = 0
+                threadlist = []
+                for tmp_slope in self.in_slews:
+                    for tmp_load in self.out_loads:
+                        thread = threading.Thread(target=characterizer.char_comb.runCombinationalDelay,
+                                args=([settings, self, harness, spice_prefix, tmp_slope, tmp_load]),
+                                name="%d" % thread_id)
+                        threadlist.append(thread)
+                        thread_id += 1
+                [thread.start() for thread in threadlist]
+                [thread.join() for thread in threadlist]
+            else:
+                # Run simulation jobs sequentially
+                for in_slew in self.in_slews:
+                    for out_load in self.out_loads:
+                        characterizer.char_comb.runCombinationalDelay(settings, self, harness, spice_prefix, in_slew, out_load)
+            # Save harness to the cell
+            unsorted_harnesses.append(harness)
+        # Filter and sort harnesses
+        # The result should be:
+        # - For each input-output path:
+        #   - 1 harness for the critical path rising case
+        #   - 1 harness for the critical path falling case
+        for output in self.out_ports:
+            for input in self.in_ports:
+                for direction in ['rise', 'fall']:
+                    # Iterate over harnesses that match output, input, and direction
+                    harnesses = [harness for harness in filter_harnesses_by_ports(unsorted_harnesses, input, output) if harness.out_direction == direction]
+                    worst_case_harness = harnesses[0]
+                    for harness in harnesses:
+                        if worst_case_harness.average_propagation_delay() < harness.average_propagation_delay():
+                            worst_case_harness = harness # This harness is worse
+                    self.harnesses.append(worst_case_harness)
+
+    def export(self, settings):
+        cell_lib = [
+            f'cell ({self.name}) {{',
+            f'  area : {self.area};',
+            f'  cell_leakage_power : {self.harnesses[0].get_leakage_power(settings.vdd.voltage, settings.units.power):.7f};', # TODO: Check whether we should use the 1st
+        ]
+        # Input ports
+        for in_port in self.in_ports:
+            cell_lib.extend([
+                f'  pin ({in_port}) {{',
+                f'    direction : input;',
+                f'    capacitance : {self.get_input_capacitance(in_port, settings.vdd.voltage, settings.units.capacitance):.7f};',
+                f'    rise_capacitance : 0;', # TODO: calculate this (average over harnesses?)
+                f'    fall_capacitance : 0;', # TODO: calculate this (average over harnesses?)
+                f'  }}', # end pin
+            ])
+        # Output ports and functions
+        for out_port in self.out_ports:
+            cell_lib.extend([
+                f'  pin ({out_port}) {{',
+                f'    direction : output;',
+                f'    capacitance : 0;', # Matches OSU350_reference, but may not be correct. TODO: Check
+                f'    rise_capacitance : 0;', # Matches OSU350_reference, but may not be correct. TODO: Check
+                f'    fall_capacitance : 0;', # Matches OSU350_reference, but may not be correct. TODO: Check
+                f'    max_capacitance : {max(self.out_loads):.7f};', # TODO: Calculate (average?)
+                f'    function : "{self.functions[self.out_ports.index(out_port)]}";'
+            ])
+            # Timing
+            for in_port in self.in_ports:
+                # Fetch harnesses which target this in_port/out_port combination
+                harnesses = filter_harnesses_by_ports(self.harnesses, in_port, out_port)
+                cell_lib.extend([
+                    f'    timing () {{',
+                    f'      related_pin : {in_port}',
+                    f'      timing_sense : {check_timing_sense(harnesses)}',
+                ])
+                for direction in ['rise', 'fall']:
+                    harness = find_harness_by_arc(self.harnesses, in_port, out_port, direction)
+                    # Propagation delay
+                    cell_lib.append(f'      {harness.direction_prop} (delay_template_{len(self.in_slews)}x{len(self.out_loads)}) {{')
+                    for line in harness.get_propagation_delay_lut(self.in_slews, self.out_loads, settings.units.time):
+                        cell_lib.append(f'        {line}')
+                    cell_lib.append(f'      }}') # end cell_rise/fall LUT
+                    # Transition delay
+                    cell_lib.append(f'      {harness.direction_tran} (delay_template_{len(self.in_slews)}x{len(self.out_loads)}) {{')
+                    for line in harness.get_transport_delay_lut(self.in_slews, self.out_loads, settings.units.time):
+                        cell_lib.append(f'        {line}')
+                    cell_lib.append(f'      }}') # end rise/fall_transition LUT
+                cell_lib.append(f'    }}') # end timing
+            # Internal power
+            for in_port in self.in_ports:
+                # Fetch harnesses which target this in_port/out_port combination
+                harnesses = filter_harnesses_by_ports(self.harnesses, in_port, out_port)
+                cell_lib.extend([
+                    f'    internal_power () {{',
+                    f'      related_pin : "{in_port}"',
+                ])
+                for direction in ['rise', 'fall']:
+                    harness = find_harness_by_arc(self.harnesses, in_port, out_port, direction)
+                    cell_lib.append(f'      {harness.direction_power} (energy_template_{len(self.in_slews)}x{len(self.out_loads)}) {{')
+                    for line in harness.get_internal_energy_lut(self.in_slews, self.out_loads, settings.energy_meas_high_threshold_voltage(), settings.units.energy, settings.units.current):
+                        cell_lib.append(f'        {line}')
+                    cell_lib.append(f'      }}') # end rise/fall_power LUT
+                cell_lib.append(f'    }}') # end internal power
+            cell_lib.append(f'  }}') # end pin
+        cell_lib.append(f'}}') # end cell
+        return '\n'.join(cell_lib)
+
+class SequentialCell(LogicCell):
+    def __init__(self, name: str, in_ports: list, out_ports: list, clock_pin: str, flops: str, function: str, **kwargs):
+        super().__init__(name, in_ports, out_ports, function, **kwargs)
+        self.set = kwargs.get('set_pin')        # set pin name
+        self.reset = kwargs.get('reset_pin')    # reset pin name
+        self.clock = clock_pin                  # clock pin name
+        self.flops = flops                      # registers
+        
+        self._clock_slew = 0
+        if 'clock_slew' in kwargs.keys():
+            self.clock_slew = kwargs['clock_slew']
+
+        self._sim_setup_highest = 0
+        self._sim_setup_lowest = 0
+        self._sim_setup_timestep = 0
+        self._sim_hold_highest = 0
+        self._sim_hold_lowest = 0
+        self._sim_hold_timestep = 0
+        if 'simulation' in kwargs.keys():
+            sim = kwargs['simulation']
+            if 'setup' in sim.keys():
+                setup = sim['setup']
+                self.sim_setup_highest = setup.get('highest')
+                self.sim_setup_lowest = setup.get('lowest')
+                self.sim_setup_timestep = setup.get('timestep')
+            if 'hold' in sim.keys():
+                hold = sim['hold']
+                self.sim_hold_highest = hold.get('highest')
+                self.sim_hold_lowest = hold.get('lowest')
+                self.sim_hold_timestep = hold.get('timestep')
+
+    def __str__(self) -> str:
+        lines = super().__str__().split('\n')
+        function_line_index = lambda : lines.index(next([line for line in lines if line.startswith('Functions:')]))
+        # Insert pin names before functions line
+        if self.clock:
+            lines.insert(function_line_index, f'Clock pin:           {self.clock}')
+        if self.set:
+            lines.insert(function_line_index, f'Set pin:             {self.set}')
+        if self.reset:
+            lines.insert(function_line_index, f'Reset pin:           {self.reset}')
+        if self.flops:
+            lines.insert(function_line_index, f'Registers:           {", ".join(self.flops)}')
+
+    def __repr__(self):
+        return f'SequentialCell({self.name},{self.in_ports},{self.out_ports},{self.clock},{self.set},{self.reset},{self.flops},{self.functions},{self. area})'
+
+    @property
+    def clock(self) -> str:
+        return self._clock
+    
+    @clock.setter
+    def clock(self, value):
+        if not isinstance(value, str):
+            raise TypeError(f'Invalid type for cell clock pin: {type(value)}')
+        else:
+            self._clock = value
+
+    @property
+    def set(self) -> str:
+        return self._set
+
+    @set.setter
+    def set(self, value):
+        self._set = str(value) if value else None
+
+    @property
+    def reset(self) -> str:
+        return self._reset
+
+    @reset.setter
+    def reset(self, value):
+        self._reset = str(value) if value else None
+
+    @property
+    def flops(self) -> list:
+        return self._flops
+
+    @flops.setter
+    def flops(self, value):
+        if isinstance(value, str):
+            self._flops = value.split()
+        elif isinstance(value, list):
+            self._flops = value
+        else:
+            raise TypeError(f'Invalid type for sequential cell flop names: {type(value)}')
+
+    @property
+    def clock_slew(self) -> float:
+        return self._clock_slew
+
+    @clock_slew.setter
+    def clock_slew(self, value):
+        if isinstance(value, (int, float)):
+            if value > 0:
+                self._clock_slew = float(value)
+            else:
+                raise ValueError('Clock slew rate must be greater than zero')
+        elif value == 'auto':
+            if not self.in_slews:
+                raise ValueError('Cannot use auto clock slew rate unless in_slews is set first!')
+            else:
+                # Use minimum slew rate
+                self._clock_slew = min(self._in_slews)
+        else:
+            raise TypeError(f'Invalid type for clock slew rate: {type(value)}')
+
+    @property
+    def sim_setup_lowest(self) -> float:
+        return self._sim_setup_lowest
+
+    @sim_setup_lowest.setter
+    def sim_setup_lowest(self, value):
+        if isinstance(value, (int, float)):
+            if value > 0:
+                self._sim_setup_lowest = float(value)
+            else:
+                raise ValueError('sim_setup_lowest must be greater than zero')
+        elif value == 'auto':
+            if not self.in_slews:
+                raise ValueError('Cannot use auto for sim_setup_lowest unless in_slews is set first!')
+            else:
+                # Use -10 * max input slew rate
+                self._sim_setup_lowest = max(self.in_slews) * -10.0
+        else:
+            raise TypeError(f'Invalid type for sim_setup_lowest: {type(value)}')
+
+    @property
+    def sim_setup_highest(self) -> float:
+        return self._sim_setup_highest
+
+    @sim_setup_highest.setter
+    def sim_setup_highest(self, value):
+        if isinstance(value, (int, float)):
+            if value > 0:
+                self._sim_setup_highest = float(value)
+            else:
+                raise ValueError('sim_setup_highest must be greater than zero')
+        elif value == 'auto':
+            if not self.in_slews:
+                raise ValueError('Cannot use auto for sim_setup_highest unless in_slews is set first!')
+            else:
+                # Use 10 * max input slew rate
+                self._sim_setup_highest = max(self.in_slews) * 10.0
+        else:
+            raise TypeError(f'Invalid type for sim_setup_highest: {type(value)}')
+
+    @property
+    def sim_setup_timestep(self) -> float:
+        return self._sim_setup_timestep
+
+    @sim_setup_timestep.setter
+    def sim_setup_timestep(self, value):
+        if isinstance(value, (int, float)):
+            if value > 0:
+                self._sim_setup_timestep = float(value)
+            else:
+                raise ValueError('sim_hold_timestep must be greater than zero')
+        elif value == 'auto':
+            if self.in_slews:
+                # 1st preference: 1/10th of minimum slew rate
+                self._sim_setup_timestep = min(self.in_slews)/10.0
+            else:
+                # Otherwise, use sim timestep
+                self._sim_setup_timestep = self.sim_timestep
+        else:
+            raise TypeError(f'Invalid type for sim_setup_timestamp: {type(value)}')
+
+    @property
+    def sim_hold_lowest(self) -> float:
+        return self._sim_hold_lowest
+
+    @sim_hold_lowest.setter
+    def sim_hold_lowest(self, value):
+        if isinstance(value, (int, float)):
+            if value > 0:
+                self._sim_hold_lowest = float(value)
+            else:
+                raise ValueError('sim_hold_lowest must be greater than zero')
+        elif value == 'auto':
+            if self.in_slews:
+                # Use -10 * min slew rate
+                self._sim_hold_lowest = min(self.in_slews) * -10.0
+            else:
+                raise ValueError('Cannot use auto for sim_hold_lowest unless in_slews is set first!')
+        else:
+            raise TypeError(f'Invalid type for sim_hold_lowest: {type(value)}')
+
+    @property
+    def sim_hold_highest(self) -> float:
+        return self._sim_hold_highest
+
+    @sim_hold_highest.setter
+    def sim_hold_highest(self, value):
+        if isinstance(value, (int, float)):
+            if value > 0:
+                self._sim_hold_highest = float(value)
+            else:
+                raise ValueError('sim_hold_highest must be greater than zero')
+        elif value == 'auto':
+            if self.in_slews:
+                # Use 10 * max slew rate
+                self._sim_hold_lowest = max(self.in_slews) * 10.0
+            else:
+                raise ValueError('Cannot use auto for sim_hold_highest unless in_slews is set first!')
+        else:
+            raise TypeError(f'Invalid type for sim_hold_highest: {type(value)}')
+
+    @property
+    def sim_hold_timestep(self) -> float:
+        return self._sim_hold_timestep
+
+    @sim_hold_timestep.setter
+    def sim_hold_timestep(self, value):
+        if isinstance(value, (int, float)):
+            if value > 0:
+                self._sim_hold_timestep = float(value)
+            else:
+                raise ValueError('sim_hold_timestep must be greater than zero')
+        elif value == 'auto':
+            if self.in_slews:
+                # 1st preference: 1/10th of minimum slew rate
+                self._sim_hold_timestep = min(self.in_slews)/10.0
+            else:
+                # Otherwise, use sim timestep
+                self._sim_hold_timestep = self.sim_timestep
+        else:
+            raise TypeError(f'Invalid type for sim_setup_timestamp: {type(value)}')
+
+    def characterize(self, settings):
+        """Run Delay, Recovery & Removal characterization for a sequential cell"""
+        unsorted_harnesses = []
+        for test_vector in self.test_vectors:
+            # Generate harness
+            harness = SequentialHarness(self, test_vector)
+            # Generate spice filename
+            spice_prefix = f'{settings.work_dir}/_{self.name}_{harness.spice_infix()}'
+            # Run simulation
+            characterizer.char_seq.runSequential(settings, self, harness, spice_prefix)
+            # Add harness to collection after characterization
+            unsorted_harnesses.append(harness)
+        # Filter and sort harnesses
+        # For clock:
+        # - No harnesses TODO: Add clock energy calculations (this will require at least 1 harness)
+        # For each input-output path:
+        # - 1 harness for setup_rising/falling
+        # - 1 harness for hold_rising/falling
+        # For each output port:
+        # - 2 harnesses characterizing path from clock to output: rise and fall
+        # - 1 harness characterizing path from reset to output
+        # - 1 harness characterizing path from set to output
+        # For reset:
+        # - 1 harness for clock to reset recovery_rising/falling
+        # - 1 harness for set to reset recovery_rising/falling
+        # - 1 harness for clock to reset removal_rising/falling
+        # For set:
+
+
+    def export(self, settings):
+        cell_lib = [
+            f'cell ({self.name}) {{',
+            f'  area : {self.area};',
+            f'  cell_leakage_power : {self.harnesses[0].get_leakage_power(settings.vdd.voltage, settings.units.power):.7f};', # TODO: Check whether we should use the 1st
+        ]
+        # Flip Flop (single flop supported per cell)
+        # TODO: This is not a very flexible way to store flops, and should be improved in the future
+        cell_lib.append(f'  ff ({",".join(self.flops)}) {{')
+        for in_port in self.in_ports:
+            cell_lib.append(f'    next_state : "{in_port}";')
+        cell_lib.append(f'    clocked_on : "{self.clock}";')
+        if self.reset:
+            cell_lib.append(f'    clear : "{self.reset}";')
+        if self.set:
+            cell_lib.append(f'    preset : "{self.set}";')
+        if self.reset:
+            cell_lib.append(f'    clear_preset_var1: L;') # Hard-coded value for when set and reset are both active
+            cell_lib.append(f'    clear_preset_var2: L;')
+        cell_lib.append(f'  }}') # end ff
+        # Clock and Input ports
+        for in_port in [self.clock, *self.in_ports]:
+            cell_lib.extend([
+                f'  pin ({in_port}) {{',
+                f'    direction : input;',
+                f'    capacitance : {self.get_input_capacitance(in_port, settings.vdd.voltage, settings.units.capacitance):.7f};',
+                f'    rise_capacitance : 0;', # TODO: Calculate this
+                f'    fall_capacitance : 0;', # TODO: Calculate this
+            ])
+            # Timing
+            if in_port is self.clock:
+                cell_lib.append(f'    clock : true;')
+            elif in_port in self.in_ports:
+                for out_port in self.out_ports:
+                    harnesses = filter_harnesses_by_ports(self.harnesses, in_port, out_port)
+                    # TODO: Fetch harnesses for setup and hold timing
+                    # TODO: Figure out which harnesses to use here (one for setup, one for hold)
+                    for harness in harnesses:
+                        cell_lib.extend([
+                            f'    timing () {{',
+                            f'      related_pin : "{self.clock}";',
+                            f'      timing_type : "{harness.timing_type}";',
+                            f'      when : "TODO";', # TODO
+                            f'      sdf_cond : "TODO";', # TODO
+                            f'      /* TODO: add rise_constraint and fall_constraint LUTs */', # TODO
+                        ])
+                        cell_lib.append(f'    }}') # end timing
+            # Internal power
+            cell_lib.extend([
+                f'    internal_power () {{',
+                f'      /* TODO: add rise_power and fall_power LUTs */', # TODO
+            ])
+            cell_lib.append(f'    }}') # end internal_power
+            # Clock pulse widths
+            if in_port is self.clock:
+                cell_lib.extend([
+                    f'    min_pulse_width_high : 0;', # TODO
+                    f'    min_pulse_width_low : 0;', # TODO
+            ])
+            cell_lib.append(f'  }}') # end pin
+        # Output ports
+        for out_port in self.out_ports:
+            cell_lib.extend([
+                f'  pin ({out_port}) {{',
+                f'    direction : output;',
+                f'    capacitance : 0;', # Matches OSU350_reference, but may not be correct. TODO: Check
+                f'    rise_capacitance : 0;', # Matches OSU350_reference, but may not be correct. TODO: Check
+                f'    fall_capacitance : 0;', # Matches OSU350_reference, but may not be correct. TODO: Check
+                f'    max_capacitance : {max(self.out_loads):.7f};', # TODO: Check (or actually calculate this)
+                f'    function : "{self.functions[self.out_ports.index(out_port)]}";'
+            ])
+            # Timing and internal power
+            related_ports = [self.clock]
+            if self.reset: related_ports.append(self.reset)
+            if self.set: related_ports.append(self.set)
+            for related_port in related_ports:
+                # TODO: Figure out how to properly fetch the right harnesses for this
+                harnesses = filter_harnesses_by_ports(self.harnesses, related_port, out_port)
+                # Timing
+                cell_lib.extend([
+                    f'    timing () {{',
+                    f'      related_pin : "{related_port}";',
+                    f'      timing_sense : "{check_timing_sense(harnesses)}";',
+                    f'      timing_type : "{harness[0].timing_type}"'
+                ])
+                # TODO: add cell_rise, rise_transition, cell_fall, and fall_transition (as appropriate)
+                cell_lib.append(f'    }}') # end timing
+                # Internal power
+                cell_lib.extend([
+                    f'    internal_power () {{',
+                    f'      related_pin : "{related_port}";',
+                ])
+                # TODO: Add rise_power/fall_power/power (as appropriate)
+                cell_lib.append(f'    }}')
+            cell_lib.append(f'  }}') # end pin
+        # Reset port
+        if self.reset:
+            cell_lib.extend([
+                f'  pin ({self.reset}) {{',
+                f'    direction : input;',
+                f'    capacitance : 0;', # TODO: Calculate this
+                f'    rise_capacitance : 0;', # TODO: Calculate this
+                f'    fall_capacitance : 0;', # TODO: Calculate this
+                f'    min_pulse_width_low : 0;' # TODO
+            ])
+            # TODO: Filter harnesses to get recovery for CLK and S, and removal for CLK
+            for harness in self.harnesses: # TODO: Change from self.harnesses (as indicated above)
+                cell_lib.extend([
+                    f'    timing () {{',
+                    f'      related_pin : "{harness.target_in_port}";',
+                    f'      timing_type : {harness.timing_type};',
+                    f'      when : "TODO";', # TODO
+                    f'      sdf_cond : "TODO";', # TODO
+                    f'      /* TODO: add rise_constraint LUT */'
+                ])
+                cell_lib.append(f'    }}') # end timing
+            cell_lib.append(f'  }}') # end pin
+        # Set port
+        if self.set:
+            cell_lib.extend([
+                f'  pin ({self.reset}) {{',
+                f'    direction : input;',
+                f'    capacitance : 0;', # TODO: Calculate this
+                f'    rise_capacitance : 0;', # TODO: Calculate this
+                f'    fall_capacitance : 0;', # TODO: Calculate this
+                f'    min_pulse_width_low : 0;' # TODO
+            ])
+            # TODO: Filter harnesses to get recovery for CLK and R, and removal for CLK
+            for harness in self.harnesses: # TODO: Change from self.harnesses (as indicated above)
+                cell_lib.extend([
+                    f'    timing () {{',
+                    f'      related_pin : "{harness.target_in_port}";',
+                    f'      timing_type : {harness.timing_type};',
+                    f'      when : "TODO";', # TODO
+                    f'      sdf_cond : "TODO";', # TODO
+                    f'      /* TODO: add rise_constraint LUT */'
+                ])
+                cell_lib.append(f'    }}') # end timing
+            cell_lib.append(f'  }}') # end pin
+        cell_lib.append(f'}}') # end cell
\ No newline at end of file
diff --git a/liberty/README.md b/liberty/README.md
new file mode 100644
index 0000000..76d7b27
--- /dev/null
+++ b/liberty/README.md
@@ -0,0 +1,3 @@
+This folder contains python tools for interfacing with liberty files.
+
+Eventually this should probably evolve into a library for reading/writing liberty files, but for our use cases this is sufficient so far.
\ No newline at end of file
diff --git a/liberty/UnitsSettings.py b/liberty/UnitsSettings.py
new file mode 100644
index 0000000..0b73787
--- /dev/null
+++ b/liberty/UnitsSettings.py
@@ -0,0 +1,324 @@
+class EngineeringUnit:
+    def __init__(self, symbol, magnitude = 1) -> None:
+        self.symbol = symbol
+        self.magnitude = magnitude
+
+    # TODO: Use a dict or similar structure to map prefixes to magnitudes instead of these awful if/else blocks
+
+    @property
+    def symbol(self):
+        return self._symbol
+
+    @symbol.setter
+    def symbol(self, value: str) -> None:
+        if value is not None and len(value) > 0:
+            self._symbol = value
+
+    def get_metric_prefix(self) -> str:
+        """Converts magnitude into a metric prefix"""
+        if self.magnitude == 1e30:
+            return 'Q'
+        elif self.magnitude == 1e27:
+            return 'R'
+        elif self.magnitude == 1e24:
+            return 'Y'
+        elif self.magnitude == 1e21:
+            return 'Z'
+        elif self.magnitude == 1e18:
+            return 'E'
+        elif self.magnitude == 1e15:
+            return 'P'
+        elif self.magnitude == 1e12:
+            return 'T'
+        elif self.magnitude == 1e9:
+            return 'G'
+        elif self.magnitude == 1e6:
+            return 'M'
+        elif self.magnitude == 1e3:
+            return 'k'
+        elif self.magnitude == 1e-3:
+            return 'm'
+        elif self.magnitude == 1e-6:
+            return 'u'
+        elif self.magnitude == 1e-9:
+            return 'n'
+        elif self.magnitude == 1e-12:
+            return 'p'
+        elif self.magnitude == 1e-15:
+            return 'f'
+        elif self.magnitude == 1e-18:
+            return 'a'
+        elif self.magnitude == 1e-21:
+            return 'z'
+        elif self.magnitude == 1e-24:
+            return 'y'
+        elif self.magnitude == 1e-27:
+            return 'r'
+        elif self.magnitude == 1e-30:
+            return 'q'
+        else:
+            return None
+
+    @property
+    def magnitude(self):
+        return self._magnitude
+
+    @magnitude.setter
+    def magnitude(self, value) -> None:
+        if value is not None:
+            if isinstance(value, float) or value == 1:
+                # TODO: Add checks to make sure value is a multiple of 10^3n
+                self._magnitude = value
+            elif isinstance(value, str):
+                if value == 'Q':
+                    self.magnitude = 1e30
+                elif value == 'R':
+                    self.magnitude = 1e27
+                elif value == 'Y':
+                    self.magnitude = 1e24
+                elif value == 'Z':
+                    self.magnitude = 1e21
+                elif value == 'E':
+                    self.magnitude = 1e18
+                elif value == 'P':
+                    self.magnitude = 1e15
+                elif value == 'T':
+                    self.magnitude = 1e12
+                elif value == 'G':
+                    self.magnitude = 1e9
+                elif value == 'M':
+                    self.magnitude = 1e6
+                elif value == 'k':
+                    self.magnitude = 1e3
+                elif value == 'm':
+                    self.magnitude = 1e-3
+                elif value == 'u':
+                    self.magnitude = 1e-6
+                elif value == 'n':
+                    self.magnitude = 1e-9
+                elif value == 'p':
+                    self.magnitude = 1e-12
+                elif value == 'f':
+                    self.magnitude = 1e-15
+                elif value == 'a':
+                    self.magnitude = 1e-18
+                elif value == 'z':
+                    self.magnitude = 1e-21
+                elif value == 'y':
+                    self.magnitude = 1e-24
+                elif value == 'r':
+                    self.magnitude = 1e-27
+                elif value == 'q':
+                    self.magnitude = 1e-30
+                else:
+                    raise ValueError("EngineeringUnit.magnitude accepts only SI units which correspond to values of 10^3N. See https://www.nist.gov/pml/owm/metric-si-prefixes")
+            else:
+                raise TypeError("EngineeringUnit.magnitude must be assigned as a float mulitple of 10 (such as 1e3) or as an SI prefix")
+        else:
+            raise TypeError("EngineeringUnit.magnitude must be assigned as an integer or SI prefix")
+                
+
+    def __str__(self) -> str:
+        # Turn magnitude into a metric prefix
+        mag_repr = self.get_metric_prefix()
+        if mag_repr is not None:
+            return f'{mag_repr}{self.symbol}'
+        elif self.magnitude == 1:
+            return self.symbol
+        else: # Handle the case where magnitude is not 1e3N
+            return f'{self.symbol} x {self.magnitude}'
+
+    def __repr__(self) -> str:
+        return f'EngineeringUnit({self.symbol}, {self.magnitude})'
+
+
+class UnitsSettings:
+    def __init__(self, **kwargs) -> None:
+        # Initialize using setters
+        self.voltage = kwargs.get('voltage', 'V')
+        self.capacitance = kwargs.get('capacitance', 'pF')
+        self.resistance = kwargs.get('resistance', 'Ω')
+        self.current = kwargs.get('current', 'uA')
+        self.time = kwargs.get('time', 'ns')
+        self.power = kwargs.get('power', 'nW')
+        self.energy = kwargs.get('energy', 'fJ')
+        # TODO: Add temperature
+
+    def __str__(self) -> str:
+        lines = []
+        lines.append(f'Voltage unit:     {str(self.voltage)}')
+        lines.append(f'Current unit:     {str(self.current)}')
+        lines.append(f'Resistance unit:  {str(self.resistance)}')
+        lines.append(f'Capacitance unit: {str(self.capacitance)}')
+        lines.append(f'Time unit:        {str(self.time)}')
+        lines.append(f'Energy unit:      {str(self.energy)}')
+        lines.append(f'Power unit:       {str(self.power)}')
+        return '\n'.join(lines)
+
+    @property
+    def voltage(self):
+        return self._voltage
+
+    @voltage.setter
+    def voltage(self, value):
+        # Valid symbols are "V" or "Volts"
+        if isinstance(value, str):
+            if value.lower().endswith('v'):
+                if value.lower() == 'v': # Case A: no SI prefix
+                    self._voltage = EngineeringUnit('V')
+                else: # Case B: SI prefix present
+                    self._voltage = EngineeringUnit(value[-1], value[:-1])
+            elif value.lower().endswith('volts'):
+                if value.lower() == 'volts': # Case A: no SI prefix
+                    self._voltage = EngineeringUnit(value)
+                else: # Case B: SI prefix present
+                    self._voltage = EngineeringUnit(value[-5:], value[:-5])
+            else:
+                raise ValueError(f'Invalid voltage unit: {value}')
+        elif isinstance(value, EngineeringUnit):
+            if value.symbol.lower() in ['v', 'volts']:
+                self._voltage = value
+
+    @property
+    def capacitance(self):
+        return self._capacitance
+
+    @capacitance.setter
+    def capacitance(self, value):
+        # Valid symbols are "F" or "Farads"
+        if isinstance(value, str):
+            if value.lower().endswith('f'):
+                if value.lower() == 'f':
+                    self._capacitance = EngineeringUnit('F')
+                else:
+                    self._capacitance = EngineeringUnit(value[-1], value[:-1])
+            elif value.lower().endswith('farads'):
+                if value.lower() == 'farads':
+                    self._capacitance = EngineeringUnit(value)
+                else:
+                    self.capacitance = EngineeringUnit(value[-6:], value[:-6])
+            else:
+                raise ValueError(f'Invalid capacitance unit: {value}')
+        elif isinstance(value, EngineeringUnit):
+            if value.symbol.lower() in ['f', 'farads']:
+                self._capacitance = value
+    
+    @property
+    def resistance(self):
+        return self._resistance
+
+    @resistance.setter
+    def resistance(self, value):
+        # Valid symbols are "Ω" or "Ohms"
+        if isinstance(value, str):
+            if value.endswith('Ω'):
+                if value == 'Ω':
+                    self._resistance = EngineeringUnit('Ω')
+                else:
+                    self._resistance = EngineeringUnit(value[-1], value[:-1])
+            elif value.lower().endswith('ohms') or value.lower().endswith('ohm'):
+                if value.lower() == 'ohms' or value.lower() == 'ohm':
+                    self._resistance = EngineeringUnit(value)
+                else:
+                    self._resistance = EngineeringUnit(value[-3:], value[:-3])
+            else:
+                raise ValueError(f'Invalid resistance unit: {value}')
+        elif isinstance(value, EngineeringUnit):
+            if value.symbol == 'Ω' or value.symbol.lower() in ['ohm', 'ohms']:
+                self._resistance = value
+    
+    @property
+    def current(self):
+        return self._current
+
+    @current.setter
+    def current(self, value):
+        # Valid symbols are "A" or "Amps"
+        if isinstance(value, str):
+            if value.lower().endswith('a'):
+                if value.lower() == 'a':
+                    self._current = EngineeringUnit('A')
+                else:
+                    self._current = EngineeringUnit(value[-1], value[:-1])
+            elif value.lower().endswith('amps'):
+                if value.lower() == 'amps':
+                    self._current = EngineeringUnit(value)
+                else:
+                    self._current = EngineeringUnit(value[-4:], value[:-4])
+            else:
+                raise ValueError(f'Invalid current unit: {value}')
+        elif isinstance(value, EngineeringUnit):
+            if value.symbol.lower() in ['a', 'amps']:
+                self._current = value
+    
+    @property
+    def time(self):
+        return self._time
+
+    @time.setter
+    def time(self, value):
+        # Valid symbols are "s" or "seconds"
+        if isinstance(value, str):
+            if value.lower().endswith('seconds'):
+                if value.lower() == 'seconds':
+                    self._time = EngineeringUnit(value)
+                else:
+                    self._time = EngineeringUnit(value[-7], value[:-7])
+            elif value.lower().endswith('s'):
+                if value.lower() == 's':
+                    self._time = EngineeringUnit(value)
+                else:
+                    self._time = EngineeringUnit(value[-1], value[:-1])
+            else:
+                raise ValueError(f'Invalid time unit: {value}')
+        elif isinstance(value, EngineeringUnit):
+            if value.symbol.lower() in ['s', 'seconds']:
+                self._time = value
+    
+    @property
+    def power(self):
+        return self._power
+    
+    @power.setter
+    def power(self, value):
+        # Valid symbols are "W" or "Watts"
+        if isinstance(value, str):
+            if value.lower().endswith('w'):
+                if value.lower() == 'w':
+                    self._power = EngineeringUnit(value)
+                else:
+                    self._power = EngineeringUnit(value[-1], value[:-1])
+            elif value.lower().endswith('watts'):
+                if value.lower() == 'watts':
+                    self._power = EngineeringUnit(value)
+                else:
+                    self._power = EngineeringUnit(value[-5:], value[:-5])
+            else:
+                raise ValueError(f'Invalid leakage power unit: {value}')
+        elif isinstance(value, EngineeringUnit):
+            if value.symbol.lower() in ['w', 'watts']:
+                self._power = value
+    
+    @property
+    def energy(self):
+        return self._energy
+    
+    @energy.setter
+    def energy(self, value):
+        # Valid symbols are "J" or "Joules"
+        if isinstance(value, str):
+            if value.lower().endswith('j'):
+                if value.lower() == 'j':
+                    self._energy = EngineeringUnit(value)
+                else:
+                    self._energy = EngineeringUnit(value[-1], value[:-1])
+            elif value.lower().endswith('joules'):
+                if value.lower() == 'joules':
+                    self._energy = EngineeringUnit(value)
+                else:
+                    self._energy = EngineeringUnit(value[-6:], value[:-6])
+            else:
+                raise ValueError(f'Invalid energy unit: {value}')
+        elif isinstance(value, EngineeringUnit):
+            if value.symbol.lower() in ['j', 'joules']:
+                self._energy = value
\ No newline at end of file


Test patch:
diff --git a/test/char_settings.json b/test/char_settings.json
deleted file mode 100644
index 1258eb6..0000000
--- a/test/char_settings.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-    "cmd_file": "osu350.cmd",
-    "lib": {
-        "name": "OSU350",
-        "dir": "spice_osu350"
-    },
-    "cell_name_prefix": "V1",
-    "cell_name_suffix": "OSU350",
-    "units": {
-        "voltage": "V",
-        "capacitance": "pF",
-        "resistance": "Ohm",
-        "current": "mA",
-        "leakage_power": "pW",
-        "energy": "fJ",
-        "time": "ns"
-    },
-    "vdd": {
-        "name": "VDD",
-        "voltage": "3.3"
-    },
-    "vss": {
-        "name": "VSS",
-        "voltage": "0"
-    },
-    "pwell": {
-        "name": "VPW",
-        "voltage": "0"
-    },
-    "nwell": {
-        "name": "VNW",
-        "voltage": "3.3"
-    }
-
-}
\ No newline at end of file
diff --git a/test/gf180/README.md b/test/gf180/README.md
new file mode 100644
index 0000000..0b4330c
--- /dev/null
+++ b/test/gf180/README.md
@@ -0,0 +1,4 @@
+This is the 0.180um GF180mcu library developed by Oklahoma State
+University for Google's open-source initiative to have an open GF180mcu PDK.
+
+
diff --git a/test/gf180/fetch_spice.sh b/test/gf180/fetch_spice.sh
new file mode 100755
index 0000000..6807e30
--- /dev/null
+++ b/test/gf180/fetch_spice.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+TARGET_DIR=${PWD}'/gf180_spice_temp'
+
+# Sources for fetching cells
+CELL_GIT_SOURCE='https://github.com/stineje/globalfoundries-pdk-libs-gf180mcu_osu_sc'
+CELL_COMMIT='df1d8ec95b'
+CELL_DIR='globalfoundries-pdk-libs-gf180mcu_osu_sc/gf180mcu_osu_sc_gp12t3v3/cells'
+
+# Source for fetching models
+MODEL_SOURCE='https://github.com/google/globalfoundries-pdk-libs-gf180mcu_fd_pr/blob/main/models/ngspice/sm141064.ngspice'
+MODEL_FILENAME='sm141064.spice'
+
+mkdir -p $TARGET_DIR
+pushd $TARGET_DIR > /dev/null
+
+# Fetch cells from a tested commit hash
+git clone $CELL_GIT_SOURCE
+cd globalfoundries-pdk-libs-gf180mcu_osu_sc/
+git checkout $CELL_COMMIT
+cd ..
+for CELL_FILE in `find "$CELL_DIR" -type f -name "*.spice"`
+do
+    cp $CELL_FILE $TARGET_DIR/.
+done
+rm -rf globalfoundries-pdk-libs-gf180mcu_osu_sc/
+
+# Fetch transistor models
+curl -s $MODEL_SOURCE > $MODEL_FILENAME
+
+popd > /dev/null
\ No newline at end of file
diff --git a/test/osu350/OSU350.cmd b/test/osu350/OSU350.cmd
new file mode 100644
index 0000000..9d99f2c
--- /dev/null
+++ b/test/osu350/OSU350.cmd
@@ -0,0 +1,55 @@
+# library settings
+set_lib_name         OSU350
+set_cell_name_suffix OSU350_
+set_cell_name_prefix _V1
+set_voltage_unit V
+set_capacitance_unit pF
+set_resistance_unit kOhm
+set_current_unit uA
+set_leakage_power_unit nW 
+set_energy_unit fJ 
+set_time_unit ns
+set_vdd_name VDD
+set_vss_name GND
+set_pwell_name VPW
+set_nwell_name VNW
+
+# characterization settings
+set_process typ
+set_temperature 25
+set_vdd_voltage 3.3
+set_vss_voltage 0
+set_pwell_voltage 0
+set_nwell_voltage 3.3
+set_logic_threshold_high 0.8
+set_logic_threshold_low 0.2
+set_logic_high_to_low_threshold 0.5
+set_logic_low_to_high_threshold 0.5
+set_work_dir work
+set_run_sim true
+set_mt_sim true
+set_suppress_message false
+set_suppress_sim_message false
+set_suppress_debug_message true
+set_energy_meas_low_threshold 0.01
+set_energy_meas_high_threshold 0.99
+set_energy_meas_time_extent 10
+set_operating_conditions typical
+
+# initialize workspace
+initialize
+
+add_cell -n INVX1 -i A -o Y -f Y=~A
+add_slope {0.015 0.04 0.08 0.2 0.4}
+add_load  {0.06 0.18 0.42 0.6 1.2}
+add_area 128
+add_netlist spice_temp/INVX1.sp
+add_model test/spice_osu350/model.sp
+add_simulation_timestep auto
+
+get_all
+
+characterize
+export
+
+exit
diff --git a/test/osu350/OSU350_reference.lib b/test/osu350/OSU350_reference.lib
new file mode 100644
index 0000000..d8f37f5
--- /dev/null
+++ b/test/osu350/OSU350_reference.lib
@@ -0,0 +1,6636 @@
+/*
+ delay model :       typ
+ check model :       typ
+ power model :       typ
+ capacitance model : typ
+ other model :       typ
+*/
+library(osu035_stdcells) {
+
+  delay_model : table_lookup;
+  in_place_swap_mode : match_footprint;
+
+  /* unit attributes */
+  time_unit : "1ns";
+  voltage_unit : "1V";
+  current_unit : "1uA";
+  pulling_resistance_unit : "1kohm";
+  leakage_power_unit : "1nW";
+  capacitive_load_unit (1,pf);
+
+  slew_upper_threshold_pct_rise : 80;
+  slew_lower_threshold_pct_rise : 20;
+  slew_upper_threshold_pct_fall : 80;
+  slew_lower_threshold_pct_fall : 20;
+  input_threshold_pct_rise : 50;
+  input_threshold_pct_fall : 50;
+  output_threshold_pct_rise : 50;
+  output_threshold_pct_fall : 50;
+  nom_process : 1;
+  nom_voltage : 3.3;
+  nom_temperature : 25;
+  operating_conditions ( typical ) {
+     process : 1;
+     voltage : 3.3;
+     temperature : 25;
+  }
+  default_operating_conditions : typical;
+
+  lu_table_template(delay_template_5x1) {
+    variable_1 : input_net_transition;
+    index_1 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0");
+  }
+  lu_table_template(delay_template_5x5) {
+    variable_1 : total_output_net_capacitance;
+    variable_2 : input_net_transition;
+    index_1 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0");
+    index_2 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0");
+  }
+  lu_table_template(delay_template_5x6) {
+    variable_1 : total_output_net_capacitance;
+    variable_2 : input_net_transition;
+    index_1 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0");
+    index_2 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+  }
+  lu_table_template(delay_template_6x1) {
+    variable_1 : input_net_transition;
+    index_1 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+  }
+  lu_table_template(delay_template_6x6) {
+    variable_1 : total_output_net_capacitance;
+    variable_2 : input_net_transition;
+    index_1 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+    index_2 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+  }
+  power_lut_template(energy_template_5x5) {
+    variable_1 : total_output_net_capacitance;
+    variable_2 : input_transition_time;
+    index_1 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0");
+    index_2 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0");
+  }
+  power_lut_template(energy_template_5x6) {
+    variable_1 : total_output_net_capacitance;
+    variable_2 : input_transition_time;
+    index_1 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0");
+    index_2 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+  }
+  power_lut_template(energy_template_6x6) {
+    variable_1 : total_output_net_capacitance;
+    variable_2 : input_transition_time;
+    index_1 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+    index_2 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+  }
+  lu_table_template(hold_template_3x5) {
+    variable_1 : related_pin_transition;
+    variable_2 : constrained_pin_transition;
+    index_1 ("1000.0, 1001.0, 1002.0");
+    index_2 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0");
+  }
+  lu_table_template(hold_template_3x6) {
+    variable_1 : related_pin_transition;
+    variable_2 : constrained_pin_transition;
+    index_1 ("1000.0, 1001.0, 1002.0");
+    index_2 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+  }
+  power_lut_template(passive_energy_template_5x1) {
+    variable_1 : input_transition_time;
+    index_1 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0");
+  }
+  power_lut_template(passive_energy_template_6x1) {
+    variable_1 : input_transition_time;
+    index_1 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+  }
+  lu_table_template(recovery_template_3x6) {
+    variable_1 : related_pin_transition;
+    variable_2 : constrained_pin_transition;
+    index_1 ("1000.0, 1001.0, 1002.0");
+    index_2 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+  }
+  lu_table_template(recovery_template_6x6) {
+    variable_1 : related_pin_transition;
+    variable_2 : constrained_pin_transition;
+    index_1 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+    index_2 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+  }
+  lu_table_template(removal_template_3x6) {
+    variable_1 : related_pin_transition;
+    variable_2 : constrained_pin_transition;
+    index_1 ("1000.0, 1001.0, 1002.0");
+    index_2 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+  }
+  lu_table_template(setup_template_3x5) {
+    variable_1 : related_pin_transition;
+    variable_2 : constrained_pin_transition;
+    index_1 ("1000.0, 1001.0, 1002.0");
+    index_2 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0");
+  }
+  lu_table_template(setup_template_3x6) {
+    variable_1 : related_pin_transition;
+    variable_2 : constrained_pin_transition;
+    index_1 ("1000.0, 1001.0, 1002.0");
+    index_2 ("1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0");
+  }
+
+/* --------------- *
+ * Design : AND2X1 *
+ * --------------- */
+cell (AND2X1) {
+area : 128;
+  cell_leakage_power : 0.0513347;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0180284;
+    rise_capacitance : 0.0179311;
+    fall_capacitance : 0.0180284;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0177842;
+    rise_capacitance : 0.0177262;
+    fall_capacitance : 0.0177842;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.411047;
+    function : "(A B)";
+    timing() {
+      related_pin : "A";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.108267, 0.115227, 0.116641, 0.115085, 0.094443", \
+          "0.158679, 0.162618, 0.169052, 0.170926, 0.152952", \
+          "0.235934, 0.242278, 0.247779, 0.251431, 0.236307", \
+          "0.468696, 0.475393, 0.482452, 0.486731, 0.476102", \
+          "0.857048, 0.863687, 0.875958, 0.875848, 0.866711");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0678, 0.0708, 0.081, 0.0852, 0.0936", \
+          "0.1338, 0.1392, 0.1434, 0.1494, 0.1584", \
+          "0.2478, 0.2508, 0.2538, 0.2568, 0.2676", \
+          "0.5982, 0.5988, 0.6006, 0.6024, 0.6108", \
+          "1.1832, 1.1826, 1.1832, 1.1844, 1.1892");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.123474, 0.150705, 0.20696, 0.245293, 0.349578", \
+          "0.167426, 0.200178, 0.257706, 0.295507, 0.406035", \
+          "0.23671, 0.269962, 0.32774, 0.366248, 0.481671", \
+          "0.442621, 0.478354, 0.535098, 0.573893, 0.691812", \
+          "0.783324, 0.81935, 0.875945, 0.91488, 1.03407");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.058568, 0.06, 0.0696, 0.0756, 0.0882", \
+          "0.111, 0.1134, 0.1218, 0.1236, 0.1368", \
+          "0.2004, 0.2004, 0.2052, 0.2064, 0.2196", \
+          "0.4806, 0.4806, 0.4812, 0.483, 0.4896", \
+          "0.9504, 0.9504, 0.9504, 0.951, 0.9546");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.107423, 0.107813, 0.099498, 0.087485, 0.034653", \
+          "0.157773, 0.157638, 0.153984, 0.144848, 0.097323", \
+          "0.234481, 0.242097, 0.232902, 0.226536, 0.181775", \
+          "0.46756, 0.474869, 0.468841, 0.460915, 0.424671", \
+          "0.855999, 0.863097, 0.857077, 0.849535, 0.815209");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0678, 0.0732, 0.0828, 0.0864, 0.102", \
+          "0.1338, 0.1392, 0.1446, 0.1506, 0.1596", \
+          "0.2478, 0.2502, 0.2544, 0.2568, 0.2718", \
+          "0.5982, 0.5982, 0.6, 0.6012, 0.6126", \
+          "1.1832, 1.1826, 1.1826, 1.1832, 1.1886");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.137572, 0.177392, 0.246292, 0.292726, 0.431469", \
+          "0.187201, 0.224568, 0.294037, 0.342471, 0.486923", \
+          "0.256505, 0.296893, 0.364747, 0.413359, 0.560093", \
+          "0.462713, 0.501671, 0.571649, 0.61918, 0.766806", \
+          "0.80352, 0.842527, 0.9126, 0.959694, 1.10837");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0606, 0.0624, 0.0744, 0.0768, 0.09", \
+          "0.1146, 0.1152, 0.1164, 0.1212, 0.1314", \
+          "0.201, 0.2004, 0.2052, 0.2058, 0.2148", \
+          "0.48, 0.4806, 0.4812, 0.4824, 0.4866", \
+          "0.9504, 0.9504, 0.9504, 0.951, 0.9534");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.069489, 0.094628, 0.172783, 0.242566, 0.48434", \
+          "0.06923, 0.096961, 0.174926, 0.23878, 0.474601", \
+          "0.073424, 0.097432, 0.174179, 0.237701, 0.46775", \
+          "0.074019, 0.098168, 0.173908, 0.236995, 0.461853", \
+          "0.074108, 0.098378, 0.171142, 0.237264, 0.459949");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.254311, 0.278202, 0.357205, 0.427364, 0.668783", \
+          "0.256995, 0.278773, 0.359187, 0.425411, 0.661228", \
+          "0.258334, 0.280603, 0.363645, 0.425442, 0.656188", \
+          "0.256529, 0.284011, 0.362775, 0.427305, 0.653995", \
+          "0.2572, 0.284942, 0.363314, 0.427933, 0.653552");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.065427, 0.086108, 0.177844, 0.254002, 0.515592", \
+          "0.063198, 0.086789, 0.179034, 0.248707, 0.504837", \
+          "0.06412, 0.091234, 0.176518, 0.244691, 0.497017", \
+          "0.065099, 0.091507, 0.172647, 0.2435, 0.490834", \
+          "0.065457, 0.09167, 0.17286, 0.243129, 0.488613");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.312337, 0.342239, 0.434071, 0.511259, 0.775046", \
+          "0.318575, 0.34494, 0.433356, 0.506027, 0.764892", \
+          "0.321264, 0.351026, 0.433373, 0.507129, 0.759555", \
+          "0.321388, 0.352838, 0.436822, 0.507263, 0.756243", \
+          "0.322341, 0.353608, 0.437701, 0.507994, 0.755192");
+      }
+    }
+  }
+}
+
+/* --------------- *
+ * Design : AND2X2 *
+ * --------------- */
+cell (AND2X2) {
+area : 128;
+  cell_leakage_power : 0.0522786;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0180033;
+    rise_capacitance : 0.0179578;
+    fall_capacitance : 0.0180033;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0177358;
+    rise_capacitance : 0.0177358;
+    fall_capacitance : 0.0177159;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.831032;
+    function : "(A B)";
+    timing() {
+      related_pin : "A";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.13066, 0.141518, 0.159321, 0.165492, 0.164762", \
+          "0.181574, 0.199647, 0.215336, 0.221674, 0.221527", \
+          "0.260293, 0.278099, 0.297021, 0.301113, 0.301757", \
+          "0.489974, 0.506691, 0.527325, 0.532019, 0.534884", \
+          "0.872706, 0.889359, 0.909559, 0.914605, 0.918514");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0762, 0.0762, 0.0828, 0.0918, 0.1014", \
+          "0.1404, 0.1398, 0.1488, 0.1494, 0.1608", \
+          "0.2496, 0.2508, 0.255, 0.2574, 0.267", \
+          "0.5928, 0.5928, 0.5952, 0.5964, 0.603", \
+          "1.17, 1.1706, 1.17, 1.1712, 1.1748");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.154045, 0.189588, 0.254769, 0.298209, 0.422264", \
+          "0.205871, 0.243003, 0.308255, 0.35275, 0.479943", \
+          "0.278538, 0.314963, 0.381175, 0.426137, 0.555773", \
+          "0.485682, 0.522106, 0.589474, 0.633809, 0.765165", \
+          "0.830154, 0.866196, 0.933544, 0.977651, 1.10933");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0702, 0.0702, 0.0822, 0.0786, 0.0966", \
+          "0.1236, 0.1206, 0.1272, 0.1326, 0.1416", \
+          "0.2088, 0.2076, 0.2118, 0.2154, 0.2268", \
+          "0.4884, 0.4884, 0.4896, 0.4908, 0.4962", \
+          "0.9642, 0.9636, 0.9642, 0.9642, 0.9666");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.130915, 0.133142, 0.134654, 0.127567, 0.091505", \
+          "0.182107, 0.187685, 0.190787, 0.185927, 0.150964", \
+          "0.26079, 0.268191, 0.270531, 0.266214, 0.234452", \
+          "0.490507, 0.497373, 0.500426, 0.496844, 0.47083", \
+          "0.873234, 0.880733, 0.883008, 0.879476, 0.854232");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0762, 0.081, 0.0852, 0.0936, 0.1038", \
+          "0.1404, 0.1434, 0.1494, 0.1548, 0.165", \
+          "0.2496, 0.2502, 0.255, 0.2568, 0.2724", \
+          "0.5928, 0.5934, 0.594, 0.5952, 0.6054", \
+          "1.17, 1.17, 1.1706, 1.1712, 1.1748");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.172065, 0.211593, 0.287991, 0.33826, 0.488621", \
+          "0.224455, 0.264242, 0.340571, 0.391515, 0.547494", \
+          "0.296521, 0.336837, 0.413898, 0.464626, 0.622647", \
+          "0.504477, 0.544344, 0.620921, 0.672375, 0.829926", \
+          "0.84905, 0.888591, 0.964821, 1.01638, 1.1743");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0696, 0.0678, 0.0804, 0.0828, 0.0984", \
+          "0.12, 0.1212, 0.126, 0.1314, 0.1446", \
+          "0.21, 0.2106, 0.213, 0.2148, 0.2238", \
+          "0.4884, 0.489, 0.4896, 0.4908, 0.4938", \
+          "0.9642, 0.9642, 0.9636, 0.9642, 0.9666");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.191386, 0.220141, 0.302119, 0.38125, 0.649766", \
+          "0.192708, 0.228606, 0.296429, 0.366311, 0.617792", \
+          "0.19518, 0.225555, 0.292713, 0.357425, 0.596341", \
+          "0.197951, 0.223997, 0.290232, 0.352333, 0.578013", \
+          "0.199091, 0.223655, 0.290196, 0.351458, 0.570641");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.352165, 0.377359, 0.461322, 0.543161, 0.801606", \
+          "0.357912, 0.382511, 0.45639, 0.527585, 0.773309", \
+          "0.360119, 0.383461, 0.455593, 0.521738, 0.757801", \
+          "0.362513, 0.384961, 0.456244, 0.519398, 0.746868", \
+          "0.363536, 0.385342, 0.457162, 0.519414, 0.742395");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.186549, 0.208247, 0.312831, 0.388838, 0.675493", \
+          "0.188994, 0.208555, 0.304332, 0.374946, 0.644439", \
+          "0.192059, 0.222091, 0.295401, 0.365145, 0.624402", \
+          "0.195221, 0.220386, 0.292889, 0.35993, 0.606836", \
+          "0.196468, 0.216487, 0.292537, 0.358451, 0.599333");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.421093, 0.458642, 0.541568, 0.624263, 0.909001", \
+          "0.42761, 0.453206, 0.536699, 0.610622, 0.88008", \
+          "0.427004, 0.453258, 0.533096, 0.604239, 0.8622", \
+          "0.427683, 0.455049, 0.531753, 0.600298, 0.848374", \
+          "0.429226, 0.456344, 0.532193, 0.600176, 0.843903");
+      }
+    }
+  }
+}
+
+/* ---------------- *
+ * Design : AOI21X1 *
+ * ---------------- */
+cell (AOI21X1) {
+area : 128;
+  cell_leakage_power : 0.034284;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0265613;
+    rise_capacitance : 0.0263291;
+    fall_capacitance : 0.0265613;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0268633;
+    rise_capacitance : 0.0265595;
+    fall_capacitance : 0.0268633;
+  }
+  pin(C)  {
+    direction : input;
+    capacitance : 0.02277;
+    rise_capacitance : 0.0227506;
+    fall_capacitance : 0.02277;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.426205;
+    function : "(!((A B)+C))";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.087618, 0.09475, 0.102291, 0.104779, 0.101472", \
+          "0.124538, 0.135237, 0.150345, 0.157869, 0.167436", \
+          "0.184919, 0.194894, 0.215981, 0.228906, 0.254643", \
+          "0.365214, 0.371165, 0.394076, 0.412908, 0.464888", \
+          "0.657342, 0.662922, 0.682715, 0.700587, 0.766988");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0882, 0.1056, 0.15, 0.1878, 0.3012", \
+          "0.1386, 0.1512, 0.1932, 0.2274, 0.3516", \
+          "0.2208, 0.2274, 0.2592, 0.2922, 0.417", \
+          "0.4722, 0.4722, 0.4854, 0.507, 0.6102", \
+          "0.8922, 0.8922, 0.894, 0.9024, 0.9648");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.099015, 0.116682, 0.147162, 0.170512, 0.238637", \
+          "0.145385, 0.163565, 0.199902, 0.227241, 0.308469", \
+          "0.219304, 0.237206, 0.277108, 0.306991, 0.401688", \
+          "0.440862, 0.45634, 0.492584, 0.525568, 0.634552", \
+          "0.806783, 0.822064, 0.853273, 0.881643, 0.992471");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1002, 0.1188, 0.1644, 0.1968, 0.3078", \
+          "0.1668, 0.177, 0.2142, 0.2496, 0.3648", \
+          "0.2772, 0.2802, 0.3072, 0.3372, 0.4476", \
+          "0.6066, 0.6066, 0.615, 0.6294, 0.7116", \
+          "1.1574, 1.1574, 1.1574, 1.1622, 1.2024");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.086164, 0.110475, 0.138653, 0.157256, 0.198103", \
+          "0.124925, 0.151978, 0.192747, 0.215703, 0.271675", \
+          "0.185335, 0.211863, 0.264991, 0.294572, 0.36979", \
+          "0.361079, 0.386852, 0.443911, 0.489563, 0.60474", \
+          "0.65428, 0.67804, 0.732408, 0.775585, 0.927407");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0852, 0.1098, 0.1512, 0.186, 0.2964", \
+          "0.1386, 0.1512, 0.204, 0.2382, 0.3576", \
+          "0.2196, 0.2262, 0.2718, 0.3108, 0.435", \
+          "0.4722, 0.4716, 0.489, 0.5184, 0.6456", \
+          "0.8922, 0.8922, 0.8922, 0.9024, 0.9948");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.084371, 0.099785, 0.118599, 0.12948, 0.161497", \
+          "0.133229, 0.148324, 0.177997, 0.197072, 0.246334", \
+          "0.20742, 0.223489, 0.257524, 0.282441, 0.352494", \
+          "0.429408, 0.444366, 0.47828, 0.50845, 0.603133", \
+          "0.796062, 0.810794, 0.840782, 0.867448, 0.970592");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0822, 0.1044, 0.1434, 0.1806, 0.2772", \
+          "0.1494, 0.1602, 0.2004, 0.231, 0.3408", \
+          "0.2562, 0.2622, 0.2916, 0.3204, 0.4278", \
+          "0.5874, 0.5868, 0.5964, 0.6108, 0.6966", \
+          "1.1376, 1.1376, 1.1382, 1.143, 1.185");
+      }
+    }
+    timing() {
+      related_pin : "C";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.070954, 0.093903, 0.122041, 0.135649, 0.170849", \
+          "0.117379, 0.147024, 0.191139, 0.215587, 0.275038", \
+          "0.187744, 0.217517, 0.278986, 0.312922, 0.399388", \
+          "0.395389, 0.423626, 0.486829, 0.53804, 0.679382", \
+          "0.73718, 0.765368, 0.825912, 0.874051, 1.04455");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.061848, 0.0843, 0.1278, 0.1572, 0.2454", \
+          "0.1194, 0.1317, 0.1827, 0.2145, 0.3192", \
+          "0.2121, 0.2163, 0.258, 0.2967, 0.4131", \
+          "0.4935, 0.4938, 0.5028, 0.5295, 0.6549", \
+          "0.9642, 0.9642, 0.9642, 0.9666, 1.0425");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.068175, 0.09122, 0.120176, 0.137237, 0.175512", \
+          "0.109921, 0.139817, 0.185467, 0.210452, 0.272412", \
+          "0.173343, 0.206154, 0.266447, 0.30177, 0.390562", \
+          "0.366349, 0.396637, 0.46394, 0.515671, 0.655905", \
+          "0.687071, 0.716557, 0.780913, 0.831257, 1.00428");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0798, 0.1095, 0.1458, 0.1713, 0.2526", \
+          "0.1374, 0.1575, 0.204, 0.234, 0.3315", \
+          "0.2337, 0.2406, 0.2838, 0.3207, 0.4296", \
+          "0.5235, 0.5229, 0.5385, 0.5652, 0.6837", \
+          "1.0065, 1.0065, 1.0071, 1.0137, 1.0923");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.100823, 0.087045, 0.006401, 0.096521, 0.424421", \
+          "0.099968, 0.091549, 0.013981, 0.065184, 0.371941", \
+          "0.098985, 0.094319, 0.032831, 0.032912, 0.309467", \
+          "0.095757, 0.095376, 0.058906, 0.012867, 0.198853", \
+          "0.095067, 0.097446, 0.074512, 0.043242, 0.112119");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.473605, 0.489085, 0.590811, 0.687265, 1.02644", \
+          "0.473461, 0.482789, 0.569246, 0.655504, 0.979048", \
+          "0.476256, 0.480142, 0.549952, 0.623455, 0.92084", \
+          "0.476311, 0.478982, 0.523227, 0.576287, 0.811951", \
+          "0.477202, 0.477839, 0.506981, 0.543667, 0.722082");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.099127, 0.08326, 0.000775, 0.076252, 0.36314", \
+          "0.09789, 0.090238, 0.021153, 0.047055, 0.315384", \
+          "0.097362, 0.09091, 0.039821, 0.017813, 0.258237", \
+          "0.096484, 0.09539, 0.063866, 0.024442, 0.158322", \
+          "0.096575, 0.097464, 0.077924, 0.051987, 0.080718");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.404524, 0.417707, 0.513502, 0.596259, 0.893842", \
+          "0.412701, 0.418437, 0.49412, 0.569094, 0.84951", \
+          "0.41945, 0.419123, 0.47871, 0.542892, 0.798752", \
+          "0.422722, 0.42352, 0.46038, 0.506418, 0.709844", \
+          "0.425263, 0.42573, 0.449881, 0.482142, 0.638213");
+      }
+    }
+    internal_power() {
+      related_pin : "C";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.104134, 0.085997, 0.017844, 0.045581, 0.272726", \
+          "0.093704, 0.087138, 0.034174, 0.019968, 0.229454", \
+          "0.086315, 0.083197, 0.046386, 0.003507, 0.182764", \
+          "0.079069, 0.078, 0.057946, 0.029295, 0.109005", \
+          "0.075771, 0.07461, 0.062624, 0.044435, 0.054769");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.266803, 0.29138, 0.365337, 0.428456, 0.649385", \
+          "0.268757, 0.285375, 0.346817, 0.402783, 0.609673", \
+          "0.270631, 0.282469, 0.330171, 0.378015, 0.565204", \
+          "0.272716, 0.278597, 0.308597, 0.342435, 0.48996", \
+          "0.27328, 0.276798, 0.295991, 0.319156, 0.429769");
+      }
+    }
+  }
+}
+
+/* ---------------- *
+ * Design : AOI22X1 *
+ * ---------------- */
+cell (AOI22X1) {
+area : 160;
+  cell_leakage_power : 0.0438097;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0265618;
+    rise_capacitance : 0.0263333;
+    fall_capacitance : 0.0265618;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0268737;
+    rise_capacitance : 0.0265584;
+    fall_capacitance : 0.0268737;
+  }
+  pin(C)  {
+    direction : input;
+    capacitance : 0.0270661;
+    rise_capacitance : 0.0270605;
+    fall_capacitance : 0.0270661;
+  }
+  pin(D)  {
+    direction : input;
+    capacitance : 0.0273706;
+    rise_capacitance : 0.0273045;
+    fall_capacitance : 0.0273706;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.422357;
+    function : "(!((A B)+(C D)))";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.106909, 0.115781, 0.128493, 0.135567, 0.14291", \
+          "0.145212, 0.15487, 0.173234, 0.184021, 0.203556", \
+          "0.20457, 0.214935, 0.23739, 0.251756, 0.285491", \
+          "0.38525, 0.391013, 0.414084, 0.433968, 0.489935", \
+          "0.677791, 0.683212, 0.703022, 0.721231, 0.789888");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.105, 0.1218, 0.1659, 0.2067, 0.3198", \
+          "0.1587, 0.1689, 0.2109, 0.2463, 0.3684", \
+          "0.2415, 0.2466, 0.2775, 0.3117, 0.4341", \
+          "0.4935, 0.4935, 0.5058, 0.5247, 0.6282", \
+          "0.9138, 0.9138, 0.9156, 0.9231, 0.9825");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.111371, 0.132737, 0.167078, 0.191499, 0.264069", \
+          "0.153817, 0.173961, 0.214255, 0.242503, 0.326191", \
+          "0.218928, 0.238535, 0.283222, 0.314807, 0.412178", \
+          "0.412229, 0.432587, 0.475431, 0.512087, 0.627626", \
+          "0.734361, 0.753947, 0.793299, 0.826904, 0.951672");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1176, 0.1332, 0.1749, 0.2073, 0.3237", \
+          "0.177, 0.1848, 0.225, 0.2598, 0.3771", \
+          "0.273, 0.2763, 0.3063, 0.3372, 0.4494", \
+          "0.5616, 0.5619, 0.5721, 0.5892, 0.681", \
+          "1.0449, 1.0446, 1.0455, 1.0515, 1.1019");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.106382, 0.13236, 0.168836, 0.189506, 0.242319", \
+          "0.144604, 0.171506, 0.218423, 0.244293, 0.310572", \
+          "0.204796, 0.231558, 0.287379, 0.319961, 0.403296", \
+          "0.381199, 0.406817, 0.4638, 0.509995, 0.631889", \
+          "0.674335, 0.698803, 0.752645, 0.795994, 0.949886");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1044, 0.1272, 0.174, 0.2109, 0.3246", \
+          "0.1581, 0.1686, 0.2223, 0.2595, 0.3786", \
+          "0.2415, 0.2454, 0.2892, 0.3288, 0.4542", \
+          "0.4935, 0.4932, 0.5079, 0.5367, 0.6654", \
+          "0.9138, 0.9135, 0.9138, 0.9219, 1.0119");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.100585, 0.119085, 0.141737, 0.156101, 0.194213", \
+          "0.143648, 0.160783, 0.194226, 0.215391, 0.270214", \
+          "0.20844, 0.225996, 0.267184, 0.293557, 0.367534", \
+          "0.402576, 0.421839, 0.462618, 0.497283, 0.5994", \
+          "0.724914, 0.744277, 0.7823, 0.814622, 0.93226");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1005, 0.1185, 0.1584, 0.1974, 0.3", \
+          "0.1608, 0.171, 0.2127, 0.2442, 0.3531", \
+          "0.2556, 0.2598, 0.2907, 0.3225, 0.4323", \
+          "0.5445, 0.5445, 0.5553, 0.5727, 0.6675", \
+          "1.0272, 1.0278, 1.0284, 1.0344, 1.0872");
+      }
+    }
+    timing() {
+      related_pin : "C";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.068551, 0.074864, 0.071423, 0.064457, 0.031897", \
+          "0.1087, 0.117473, 0.12521, 0.125516, 0.111597", \
+          "0.169619, 0.179278, 0.195645, 0.202872, 0.210214", \
+          "0.349303, 0.35577, 0.378008, 0.394453, 0.434701", \
+          "0.642286, 0.648544, 0.667744, 0.685318, 0.744546");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.068278, 0.0909, 0.1335, 0.1653, 0.2556", \
+          "0.1197, 0.1344, 0.1743, 0.2055, 0.3126", \
+          "0.2013, 0.2091, 0.2418, 0.273, 0.3858", \
+          "0.4536, 0.4539, 0.4674, 0.4878, 0.5868", \
+          "0.873, 0.873, 0.8754, 0.8838, 0.9456");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.090762, 0.124192, 0.176377, 0.211966, 0.318488", \
+          "0.130527, 0.165816, 0.228193, 0.269286, 0.390606", \
+          "0.195948, 0.229228, 0.300907, 0.34764, 0.485438", \
+          "0.388654, 0.419843, 0.490637, 0.547081, 0.717716", \
+          "0.709605, 0.73957, 0.805851, 0.858985, 1.04713");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1044, 0.1254, 0.1656, 0.1926, 0.2793", \
+          "0.1638, 0.1737, 0.2199, 0.2493, 0.3465", \
+          "0.2589, 0.2634, 0.3, 0.3318, 0.4353", \
+          "0.5484, 0.5487, 0.5598, 0.5823, 0.6867", \
+          "1.0323, 1.0317, 1.032, 1.0377, 1.104");
+      }
+    }
+    timing() {
+      related_pin : "D";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.068235, 0.086318, 0.10175, 0.106816, 0.111444", \
+          "0.108152, 0.133515, 0.164053, 0.177637, 0.204588", \
+          "0.168204, 0.195279, 0.242155, 0.265441, 0.31821", \
+          "0.344946, 0.371698, 0.4277, 0.47127, 0.572169", \
+          "0.639948, 0.6641, 0.717219, 0.759988, 0.905949");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.068248, 0.0942, 0.1359, 0.1644, 0.2556", \
+          "0.1194, 0.1338, 0.1851, 0.2166, 0.3234", \
+          "0.2031, 0.207, 0.255, 0.291, 0.4071", \
+          "0.4533, 0.4539, 0.4713, 0.5022, 0.6276", \
+          "0.873, 0.873, 0.8736, 0.8832, 0.9774");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.078061, 0.105426, 0.147103, 0.172747, 0.243204", \
+          "0.119963, 0.152406, 0.205875, 0.239787, 0.330752", \
+          "0.184706, 0.217548, 0.283524, 0.324879, 0.439751", \
+          "0.378973, 0.409134, 0.478102, 0.53253, 0.690209", \
+          "0.700613, 0.729821, 0.79489, 0.846754, 1.0292");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0852, 0.1098, 0.1482, 0.1713, 0.2526", \
+          "0.1461, 0.1593, 0.2055, 0.2322, 0.3276", \
+          "0.2418, 0.2457, 0.2862, 0.3195, 0.4221", \
+          "0.5313, 0.5316, 0.5436, 0.5664, 0.6762", \
+          "1.0146, 1.0146, 1.0149, 1.0203, 1.0905");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.08862, 0.077942, 0.009309, 0.094769, 0.416256", \
+          "0.087155, 0.081498, 0.00904, 0.069347, 0.371149", \
+          "0.086437, 0.081108, 0.023659, 0.041628, 0.315723", \
+          "0.083247, 0.082654, 0.046296, 0.01383, 0.213842", \
+          "0.082383, 0.084215, 0.060908, 0.028818, 0.130631");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.574879, 0.58766, 0.686407, 0.777311, 1.11607", \
+          "0.573858, 0.582092, 0.665057, 0.748138, 1.07063", \
+          "0.575903, 0.580414, 0.647797, 0.718789, 1.01427", \
+          "0.578132, 0.580154, 0.623373, 0.67411, 0.908349", \
+          "0.579062, 0.579264, 0.608528, 0.64349, 0.820706");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.086888, 0.077144, 0.006265, 0.075576, 0.355605", \
+          "0.085484, 0.08022, 0.014359, 0.05201, 0.314743", \
+          "0.084649, 0.079307, 0.029782, 0.02673, 0.264414", \
+          "0.083459, 0.082606, 0.051245, 0.013287, 0.172953", \
+          "0.083621, 0.084198, 0.064426, 0.037771, 0.098546");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.504789, 0.5202, 0.607008, 0.687078, 0.982391", \
+          "0.511868, 0.517758, 0.590948, 0.662743, 0.940649", \
+          "0.518589, 0.519778, 0.577438, 0.638741, 0.892104", \
+          "0.525042, 0.524655, 0.560862, 0.604726, 0.806265", \
+          "0.527668, 0.52723, 0.551626, 0.582048, 0.736708");
+      }
+    }
+    internal_power() {
+      related_pin : "C";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.103246, 0.080197, 0.009441, 0.092711, 0.391211", \
+          "0.093989, 0.082217, 0.008749, 0.064219, 0.342882", \
+          "0.087005, 0.079435, 0.024106, 0.037093, 0.287374", \
+          "0.079894, 0.074891, 0.04103, 0.002314, 0.193829", \
+          "0.076237, 0.072442, 0.05063, 0.021537, 0.122637");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.382637, 0.412968, 0.505327, 0.586004, 0.877529", \
+          "0.383556, 0.403556, 0.484526, 0.557975, 0.833523", \
+          "0.385631, 0.399852, 0.464029, 0.528555, 0.781062", \
+          "0.387493, 0.39469, 0.436012, 0.48254, 0.685772", \
+          "0.388573, 0.392556, 0.418669, 0.450603, 0.604142");
+      }
+    }
+    internal_power() {
+      related_pin : "D";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.100747, 0.081524, 0.004462, 0.076373, 0.344227", \
+          "0.092456, 0.080816, 0.016033, 0.049265, 0.297734", \
+          "0.085654, 0.078117, 0.03005, 0.023541, 0.245356", \
+          "0.078806, 0.074639, 0.046256, 0.009926, 0.159509", \
+          "0.07486, 0.072485, 0.054778, 0.030343, 0.094374");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.31926, 0.344088, 0.429797, 0.502557, 0.760593", \
+          "0.324361, 0.340262, 0.411143, 0.477624, 0.721729", \
+          "0.329665, 0.339745, 0.395094, 0.452168, 0.676577", \
+          "0.335571, 0.33954, 0.373924, 0.414908, 0.595218", \
+          "0.338322, 0.340362, 0.362063, 0.390105, 0.527079");
+      }
+    }
+  }
+}
+
+/* -------------- *
+ * Design : BUFX2 *
+ * -------------- */
+cell (BUFX2) {
+  cell_footprint : buf;
+area : 96;
+  cell_leakage_power : 0.0381689;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0134147;
+    rise_capacitance : 0.0133626;
+    fall_capacitance : 0.0134147;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.831224;
+    function : "A";
+    timing() {
+      related_pin : "A";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.133137, 0.154559, 0.190018, 0.203029, 0.231747", \
+          "0.18185, 0.207778, 0.240227, 0.257765, 0.289299", \
+          "0.259107, 0.284588, 0.318472, 0.334377, 0.36909", \
+          "0.489567, 0.516198, 0.547039, 0.562723, 0.597803", \
+          "0.872314, 0.898781, 0.929312, 0.944672, 0.979724");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0726, 0.0732, 0.0852, 0.0954, 0.1068", \
+          "0.1374, 0.138, 0.144, 0.15, 0.1626", \
+          "0.2478, 0.2478, 0.2514, 0.2532, 0.264", \
+          "0.5928, 0.5928, 0.5928, 0.594, 0.5982", \
+          "1.17, 1.17, 1.1706, 1.1706, 1.1724");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.145384, 0.178566, 0.231467, 0.270548, 0.365483", \
+          "0.193553, 0.229676, 0.287701, 0.323216, 0.421557", \
+          "0.265945, 0.302361, 0.359906, 0.395447, 0.49735", \
+          "0.473948, 0.509259, 0.567157, 0.60388, 0.706799", \
+          "0.818489, 0.853514, 0.911751, 0.947501, 1.05108");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0696, 0.0702, 0.0756, 0.084, 0.0942", \
+          "0.1188, 0.123, 0.126, 0.1308, 0.1452", \
+          "0.2076, 0.2076, 0.2124, 0.2136, 0.228", \
+          "0.489, 0.4884, 0.4896, 0.4914, 0.4974", \
+          "0.9642, 0.9636, 0.9636, 0.9648, 0.9672");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.202271, 0.224707, 0.298267, 0.362152, 0.597273", \
+          "0.200651, 0.219543, 0.287291, 0.347882, 0.564936", \
+          "0.200676, 0.217103, 0.282401, 0.336491, 0.545011", \
+          "0.19765, 0.216643, 0.278566, 0.329387, 0.525948", \
+          "0.198941, 0.217398, 0.277368, 0.327414, 0.517438");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.311936, 0.339705, 0.42546, 0.48532, 0.72018", \
+          "0.319514, 0.339947, 0.412677, 0.471692, 0.688796", \
+          "0.322872, 0.345658, 0.409983, 0.464941, 0.67147", \
+          "0.326115, 0.346682, 0.409135, 0.462979, 0.658831", \
+          "0.327123, 0.347526, 0.410173, 0.4626, 0.654153");
+      }
+    }
+  }
+}
+
+/* -------------- *
+ * Design : BUFX4 *
+ * -------------- */
+cell (BUFX4) {
+  cell_footprint : buf;
+area : 128;
+  cell_leakage_power : 0.0543119;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0204034;
+    rise_capacitance : 0.0203782;
+    fall_capacitance : 0.0204034;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 1.66099;
+    function : "A";
+    timing() {
+      related_pin : "A";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.06, 0.16, 0.32, 0.8, 1.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.150497, 0.17864, 0.215212, 0.235777, 0.278738", \
+          "0.203356, 0.232307, 0.271157, 0.292104, 0.337126", \
+          "0.281752, 0.309697, 0.34909, 0.370388, 0.417398", \
+          "0.512861, 0.539664, 0.578386, 0.598535, 0.645108", \
+          "0.895447, 0.92211, 0.9604, 0.980318, 1.02619");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.06, 0.16, 0.32, 0.8, 1.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0834, 0.0834, 0.0918, 0.0984, 0.1116", \
+          "0.1428, 0.1446, 0.1494, 0.1554, 0.1668", \
+          "0.2508, 0.2514, 0.255, 0.2562, 0.2676", \
+          "0.594, 0.594, 0.5946, 0.5952, 0.5994", \
+          "1.1712, 1.1712, 1.1712, 1.1712, 1.173");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.06, 0.16, 0.32, 0.8, 1.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.162386, 0.200466, 0.262237, 0.29644, 0.398076", \
+          "0.216856, 0.253596, 0.315342, 0.352882, 0.458057", \
+          "0.290174, 0.326517, 0.38822, 0.427141, 0.534734", \
+          "0.498213, 0.533642, 0.596155, 0.634742, 0.742901", \
+          "0.842732, 0.877791, 0.940266, 0.978316, 1.08687");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.06, 0.16, 0.32, 0.8, 1.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0744, 0.072, 0.0822, 0.0852, 0.1032", \
+          "0.1254, 0.1266, 0.1308, 0.1386, 0.1536", \
+          "0.21, 0.2124, 0.2148, 0.2196, 0.2304", \
+          "0.4908, 0.4902, 0.4914, 0.4926, 0.498", \
+          "0.9654, 0.9654, 0.9654, 0.966, 0.969");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.06, 0.16, 0.32, 0.8, 1.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.419664, 0.460709, 0.591849, 0.700961, 1.09146", \
+          "0.422118, 0.448441, 0.566528, 0.660692, 1.01434", \
+          "0.4253, 0.446926, 0.544091, 0.639169, 0.965864", \
+          "0.430336, 0.451156, 0.534922, 0.619211, 0.917899", \
+          "0.431139, 0.45054, 0.533304, 0.611753, 0.896223");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.06, 0.16, 0.32, 0.8, 1.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.586039, 0.630263, 0.746719, 0.856922, 1.24606", \
+          "0.58888, 0.627326, 0.726733, 0.824342, 1.17152", \
+          "0.585971, 0.62438, 0.718648, 0.80387, 1.12725", \
+          "0.592444, 0.624051, 0.712925, 0.79305, 1.09202", \
+          "0.595263, 0.624501, 0.713627, 0.790707, 1.07935");
+      }
+    }
+  }
+}
+
+/* ---------------- *
+ * Design : CLKBUF1 *
+ * ---------------- */
+cell (CLKBUF1) {
+  cell_footprint : buf;
+area : 288;
+  cell_leakage_power : 0.165423;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0549205;
+    rise_capacitance : 0.0548901;
+    fall_capacitance : 0.0549205;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 1.66707;
+    function : "A";
+    timing() {
+      related_pin : "A";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.224332, 0.243111, 0.264592, 0.276805, 0.286372, 0.290021", \
+          "0.417271, 0.440455, 0.455374, 0.474513, 0.478431, 0.482835", \
+          "0.751644, 0.775047, 0.79633, 0.809058, 0.818679, 0.823959", \
+          "1.61299, 1.63627, 1.65805, 1.67039, 1.67992, 1.68522", \
+          "2.09146, 2.11495, 2.13666, 2.14892, 2.15859, 2.16383", \
+          "2.57016, 2.5936, 2.61494, 2.62757, 2.63722, 2.64244");
+      }
+      rise_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.096, 0.0942, 0.0912, 0.09, 0.0912, 0.0948", \
+          "0.375, 0.3744, 0.375, 0.3744, 0.375, 0.375", \
+          "0.8802, 0.8802, 0.8802, 0.8802, 0.8802, 0.8802", \
+          "2.1804, 2.1798, 2.1804, 2.1804, 2.1804, 2.1798", \
+          "2.9028, 2.9028, 2.9016, 2.9028, 2.9028, 2.9022", \
+          "3.6258, 3.6258, 3.624, 3.6252, 3.6258, 3.6246");
+      }
+      cell_fall(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.232224, 0.267876, 0.30847, 0.363348, 0.400538, 0.463001", \
+          "0.406571, 0.441926, 0.485759, 0.540694, 0.578987, 0.636862", \
+          "0.707684, 0.743121, 0.785839, 0.841971, 0.880349, 0.941556", \
+          "1.48242, 1.51791, 1.56053, 1.61659, 1.65509, 1.71625", \
+          "1.91225, 1.94772, 1.99073, 2.04644, 2.0852, 2.1464", \
+          "2.34265, 2.37815, 2.42065, 2.47674, 2.51574, 2.5763");
+      }
+      fall_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.0798, 0.0792, 0.0804, 0.0804, 0.0804, 0.0804", \
+          "0.3084, 0.3084, 0.3078, 0.309, 0.3084, 0.3078", \
+          "0.7242, 0.7242, 0.7236, 0.7236, 0.7242, 0.7242", \
+          "1.7964, 1.7964, 1.7958, 1.7964, 1.7964, 1.7958", \
+          "2.391, 2.391, 2.3916, 2.391, 2.3916, 2.3916", \
+          "2.9868, 2.9868, 2.988, 2.9862, 2.988, 2.9874");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "1.11593, 1.27601, 1.63949, 2.21709, 2.66388, 3.51996", \
+          "1.17146, 1.29221, 1.64495, 2.21995, 2.66684, 3.52434", \
+          "1.17342, 1.29647, 1.64745, 2.22163, 2.66176, 3.51406", \
+          "1.17423, 1.29854, 1.63412, 2.2232, 2.66429, 3.51668", \
+          "1.17513, 1.2987, 1.63467, 2.22403, 2.66479, 3.51694", \
+          "1.17481, 1.2992, 1.63521, 2.22395, 2.66526, 3.51751");
+      }
+      fall_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "1.79422, 1.91322, 2.24419, 2.82487, 3.28384, 4.18362", \
+          "1.8097, 1.93625, 2.2816, 2.86427, 3.30394, 4.18368", \
+          "1.81377, 1.93928, 2.2801, 2.86625, 3.30861, 4.17442", \
+          "1.81636, 1.94165, 2.28099, 2.86685, 3.31017, 4.17568", \
+          "1.81642, 1.94184, 2.28129, 2.86718, 3.31079, 4.17561", \
+          "1.81749, 1.94222, 2.2816, 2.86748, 3.31121, 4.17631");
+      }
+    }
+  }
+}
+
+/* ---------------- *
+ * Design : CLKBUF2 *
+ * ---------------- */
+cell (CLKBUF2) {
+  cell_footprint : buf;
+area : 416;
+  cell_leakage_power : 0.257476;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0549339;
+    rise_capacitance : 0.0549339;
+    fall_capacitance : 0.0549141;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 1.66571;
+    function : "A";
+    timing() {
+      related_pin : "A";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.321199, 0.343529, 0.363213, 0.376051, 0.385003, 0.391058", \
+          "0.518808, 0.536599, 0.554454, 0.573676, 0.577216, 0.581919", \
+          "0.853279, 0.873575, 0.895214, 0.908146, 0.917184, 0.922869", \
+          "1.71446, 1.73599, 1.75621, 1.76933, 1.77838, 1.7841", \
+          "2.19313, 2.21467, 2.23518, 2.24801, 2.25704, 2.26281", \
+          "2.67178, 2.69332, 2.71364, 2.72665, 2.73567, 2.74132");
+      }
+      rise_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.09, 0.093, 0.09, 0.09, 0.093, 0.0936", \
+          "0.375, 0.3744, 0.3756, 0.375, 0.3744, 0.375", \
+          "0.8802, 0.8796, 0.8796, 0.8802, 0.8802, 0.8802", \
+          "2.1798, 2.1798, 2.1798, 2.1798, 2.1798, 2.1798", \
+          "2.9028, 2.9028, 2.9016, 2.9028, 2.9028, 2.9022", \
+          "3.6252, 3.6252, 3.6252, 3.6252, 3.6258, 3.6246");
+      }
+      cell_fall(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.331235, 0.369022, 0.406196, 0.462716, 0.498865, 0.56249", \
+          "0.505866, 0.541977, 0.586624, 0.64, 0.677712, 0.736427", \
+          "0.810551, 0.842957, 0.887983, 0.940963, 0.97894, 1.03761", \
+          "1.58542, 1.61764, 1.66286, 1.71555, 1.75337, 1.81231", \
+          "2.01526, 2.0474, 2.09262, 2.1454, 2.18367, 2.24238", \
+          "2.44561, 2.4778, 2.52305, 2.57567, 2.61361, 2.6724");
+      }
+      fall_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.0798, 0.0804, 0.0762, 0.0798, 0.0798, 0.0792", \
+          "0.3078, 0.309, 0.309, 0.3084, 0.3084, 0.309", \
+          "0.7242, 0.7242, 0.7242, 0.7236, 0.7242, 0.7236", \
+          "1.7958, 1.7964, 1.7964, 1.7964, 1.7964, 1.7964", \
+          "2.3916, 2.391, 2.391, 2.391, 2.3916, 2.3916", \
+          "2.9874, 2.9868, 2.9868, 2.9862, 2.988, 2.9862");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "2.01187, 2.17062, 2.53108, 3.0698, 3.56216, 4.406", \
+          "2.01007, 2.17916, 2.53623, 3.06713, 3.5648, 4.4108", \
+          "2.0137, 2.14572, 2.53166, 3.07087, 3.55276, 4.39416", \
+          "2.01546, 2.17835, 2.53421, 3.07243, 3.55459, 4.39658", \
+          "2.01565, 2.17912, 2.53484, 3.07268, 3.55546, 4.39685", \
+          "2.01643, 2.17838, 2.5351, 3.07308, 3.55579, 4.39802");
+      }
+      fall_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "2.70066, 2.81519, 3.10037, 3.68829, 4.15259, 5.08299", \
+          "2.71164, 2.82137, 3.12019, 3.71744, 4.16655, 5.0839", \
+          "2.68872, 2.82243, 3.12471, 3.71938, 4.16762, 5.09126", \
+          "2.69201, 2.82328, 3.12693, 3.71956, 4.16839, 5.09384", \
+          "2.69213, 2.82321, 3.12775, 3.71982, 4.16844, 5.0937", \
+          "2.69309, 2.82409, 3.12743, 3.71942, 4.16919, 5.09352");
+      }
+    }
+  }
+}
+
+/* ---------------- *
+ * Design : CLKBUF3 *
+ * ---------------- */
+cell (CLKBUF3) {
+  cell_footprint : buf;
+area : 544;
+  cell_leakage_power : 0.349527;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0549337;
+    rise_capacitance : 0.0549337;
+    fall_capacitance : 0.0549228;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 1.66565;
+    function : "A";
+    timing() {
+      related_pin : "A";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.420438, 0.443171, 0.462181, 0.475299, 0.483621, 0.490154", \
+          "0.618136, 0.635526, 0.655634, 0.673013, 0.676178, 0.681177", \
+          "0.952623, 0.969882, 0.990146, 1.0075, 1.01599, 1.02169", \
+          "1.81382, 1.83374, 1.85134, 1.8687, 1.87717, 1.88291", \
+          "2.29249, 2.31248, 2.32995, 2.34738, 2.3558, 2.36161", \
+          "2.77115, 2.79077, 2.80858, 2.82602, 2.83438, 2.83988");
+      }
+      rise_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.0906, 0.0942, 0.0936, 0.0906, 0.0924, 0.0936", \
+          "0.375, 0.3744, 0.3744, 0.375, 0.3744, 0.375", \
+          "0.8802, 0.8802, 0.8802, 0.8802, 0.8802, 0.8796", \
+          "2.1798, 2.1804, 2.1804, 2.1798, 2.1804, 2.1804", \
+          "2.9028, 2.9022, 2.9028, 2.9028, 2.9028, 2.9022", \
+          "3.6252, 3.6258, 3.6258, 3.6252, 3.6246, 3.6258");
+      }
+      cell_fall(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.431168, 0.464082, 0.505975, 0.56074, 0.600775, 0.661525", \
+          "0.605199, 0.642286, 0.683384, 0.736658, 0.772834, 0.835433", \
+          "0.906277, 0.943592, 0.984739, 1.03799, 1.07401, 1.13661", \
+          "1.68098, 1.71847, 1.75965, 1.8126, 1.84874, 1.91132", \
+          "2.11095, 2.14863, 2.19, 2.24248, 2.27868, 2.3412", \
+          "2.54127, 2.57896, 2.61993, 2.67301, 2.70901, 2.77138");
+      }
+      fall_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.0798, 0.0798, 0.0798, 0.0828, 0.0798, 0.0792", \
+          "0.3084, 0.3084, 0.309, 0.3078, 0.309, 0.309", \
+          "0.7242, 0.7236, 0.7236, 0.7236, 0.7242, 0.7242", \
+          "1.7964, 1.7958, 1.7964, 1.7964, 1.7964, 1.7964", \
+          "2.391, 2.3916, 2.3916, 2.3916, 2.391, 2.3916", \
+          "2.9874, 2.988, 2.988, 2.9862, 2.9874, 2.9862");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "2.8632, 3.00127, 3.40811, 3.92053, 4.46223, 5.29368", \
+          "2.85991, 3.04573, 3.41615, 3.91754, 4.46447, 5.29839", \
+          "2.86386, 3.04668, 3.41771, 3.92175, 4.4477, 5.2994", \
+          "2.86579, 3.00475, 3.41845, 3.92406, 4.45015, 5.30123", \
+          "2.86674, 3.0051, 3.41906, 3.92505, 4.45045, 5.30213", \
+          "2.86755, 3.00559, 3.41874, 3.92502, 4.45071, 5.30228");
+      }
+      fall_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "3.59541, 3.65815, 3.95854, 4.52621, 5.03298, 5.98166", \
+          "3.60073, 3.66618, 3.97078, 4.57387, 5.05721, 5.98513", \
+          "3.60534, 3.67055, 3.97511, 4.57645, 5.06066, 5.99037", \
+          "3.60829, 3.6729, 3.97688, 4.57773, 5.06339, 5.99294", \
+          "3.60853, 3.67397, 3.97745, 4.57773, 5.06401, 5.99383", \
+          "3.60942, 3.67415, 3.97778, 4.57938, 5.06429, 5.99409");
+      }
+    }
+  }
+}
+
+/* ----------------- *
+ * Design : DFFNEGX1 *
+ * ----------------- */
+cell (DFFNEGX1) {
+area : 384;
+  cell_leakage_power : 0.113357;
+  ff (DS0000,P0002) {
+    next_state : "D";
+    clocked_on : "(!CLK)";
+  }
+  pin(CLK)  {
+    direction : input;
+    capacitance : 0.04321;
+    rise_capacitance : 0.0427558;
+    fall_capacitance : 0.04321;
+    clock : true;
+    internal_power() {
+      rise_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0.163587, 0.216373, 0.297888, 0.575101, 0.776867, 1.21926");
+      }
+      fall_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0.436919, 0.547759, 0.694889, 0.961071, 1.16, 1.57122");
+      }
+    }
+    min_pulse_width_high : 0.140686;
+    min_pulse_width_low : 0.163576;
+  }
+  pin(D)  {
+    direction : input;
+    capacitance : 0.013091;
+    rise_capacitance : 0.0129706;
+    fall_capacitance : 0.013091;
+    internal_power() {
+      rise_power(passive_energy_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.16207, 0.177108, 0.229913, 0.283368, 0.463246");
+      }
+      fall_power(passive_energy_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.518077, 0.535304, 0.586976, 0.642473, 0.828541");
+      }
+    }
+    timing() {
+      related_pin : "CLK";
+      timing_type : hold_falling;
+      rise_constraint(hold_template_3x5) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "-0.09375, -0.0875, -0.075, -0.1125, -0.175", \
+          "-0.2, -0.19375, -0.18125, -0.21875, -0.1875", \
+          "-0.16875, -0.25625, -0.24375, -0.28125, -0.25");
+      }
+      fall_constraint(hold_template_3x5) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "-0.09375, -0.0875, -0.16875, -0.20625, -0.26875", \
+          "-0.0125, -0.1, -0.0875, -0.125, -0.1875", \
+          "-0.075, -0.06875, -0.05625, -0.09375, -0.15625");
+      }
+    }
+    timing() {
+      related_pin : "CLK";
+      timing_type : setup_falling;
+      rise_constraint(setup_template_3x5) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.28125, 0.275, 0.2625, 0.3, 0.3625", \
+          "0.29375, 0.2875, 0.36875, 0.3125, 0.375", \
+          "0.35625, 0.35, 0.3375, 0.375, 0.4375");
+      }
+      fall_constraint(setup_template_3x5) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.28125, 0.275, 0.35625, 0.39375, 0.45625", \
+          "0.29375, 0.2875, 0.36875, 0.40625, 0.46875", \
+          "0.2625, 0.35, 0.3375, 0.375, 0.4375");
+      }
+    }
+  }
+  pin(Q)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.808965;
+    function : "DS0000";
+    timing() {
+      related_pin : "CLK";
+      timing_sense : non_unate;
+      timing_type : falling_edge;
+      cell_rise(delay_template_5x6) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.202117, 0.235682, 0.278481, 0.330026, 0.363478, 0.413314", \
+          "0.233234, 0.267436, 0.309217, 0.361196, 0.393216, 0.445908", \
+          "0.278169, 0.312131, 0.354152, 0.406801, 0.438335, 0.490417", \
+          "0.400645, 0.43358, 0.474955, 0.527766, 0.559505, 0.613427", \
+          "0.593235, 0.626181, 0.667405, 0.719368, 0.753264, 0.806306");
+      }
+      rise_transition(delay_template_5x6) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.081, 0.0786, 0.0738, 0.0774, 0.0786, 0.0828", \
+          "0.1134, 0.1098, 0.111, 0.1086, 0.1098, 0.1122", \
+          "0.1662, 0.165, 0.1626, 0.1632, 0.1626, 0.1662", \
+          "0.3282, 0.3276, 0.3264, 0.327, 0.3282, 0.3294", \
+          "0.6108, 0.6108, 0.6108, 0.6102, 0.6102, 0.6114");
+      }
+      cell_fall(delay_template_5x6) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.198542, 0.249691, 0.309001, 0.404914, 0.463968, 0.569663", \
+          "0.233985, 0.283559, 0.344772, 0.439081, 0.501044, 0.609074", \
+          "0.282365, 0.330228, 0.393398, 0.487308, 0.547648, 0.657172", \
+          "0.399168, 0.447622, 0.511789, 0.606075, 0.668496, 0.781668", \
+          "0.575754, 0.62385, 0.687193, 0.784757, 0.846136, 0.961283");
+      }
+      fall_transition(delay_template_5x6) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.0756, 0.0786, 0.0858, 0.1056, 0.117, 0.1254", \
+          "0.1098, 0.108, 0.1158, 0.1308, 0.1398, 0.1554", \
+          "0.156, 0.1512, 0.162, 0.171, 0.1776, 0.1926", \
+          "0.2832, 0.285, 0.2892, 0.2964, 0.3036, 0.318", \
+          "0.51, 0.51, 0.5124, 0.5196, 0.5232, 0.5328");
+      }
+    }
+    internal_power() {
+      related_pin : "CLK";
+      rise_power(energy_template_5x6) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.764154, 0.854058, 1.03011, 1.31441, 1.52037, 1.92556", \
+          "0.742505, 0.84881, 1.01612, 1.3007, 1.50941, 1.91203", \
+          "0.737126, 0.840224, 0.999697, 1.28675, 1.49052, 1.90406", \
+          "0.729767, 0.835803, 0.994157, 1.28113, 1.48556, 1.89454", \
+          "0.72581, 0.831138, 0.99214, 1.27818, 1.4812, 1.89133");
+      }
+      fall_power(energy_template_5x6) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.757465, 0.897208, 1.11154, 1.52514, 1.82082, 2.39776", \
+          "0.743543, 0.87883, 1.08134, 1.48923, 1.78168, 2.35525", \
+          "0.734104, 0.865228, 1.06518, 1.45852, 1.74795, 2.31759", \
+          "0.730865, 0.857216, 1.05072, 1.42877, 1.70803, 2.27226", \
+          "0.727549, 0.853525, 1.04292, 1.41506, 1.68628, 2.23822");
+      }
+    }
+  }
+}
+
+/* ----------------- *
+ * Design : DFFPOSX1 *
+ * ----------------- */
+cell (DFFPOSX1) {
+area : 384;
+  cell_leakage_power : 0.112978;
+  ff (DS0000,P0002) {
+    next_state : "D";
+    clocked_on : "CLK";
+  }
+  pin(CLK)  {
+    direction : input;
+    capacitance : 0.0405158;
+    rise_capacitance : 0.0403976;
+    fall_capacitance : 0.0405158;
+    clock : true;
+    internal_power() {
+      rise_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0.064753, 0.023177, 0.164137, 0.430755, 0.628444, 1.02121");
+      }
+      fall_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0.552255, 0.681659, 0.849137, 1.17901, 1.40943, 1.87724");
+      }
+    }
+    min_pulse_width_high : 0.166496;
+    min_pulse_width_low : 0.151234;
+  }
+  pin(D)  {
+    direction : input;
+    capacitance : 0.0130794;
+    rise_capacitance : 0.0130318;
+    fall_capacitance : 0.0130794;
+    internal_power() {
+      rise_power(passive_energy_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.21215, 0.222443, 0.274423, 0.326727, 0.505608");
+      }
+      fall_power(passive_energy_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.473607, 0.489211, 0.536851, 0.588019, 0.781043");
+      }
+    }
+    timing() {
+      related_pin : "CLK";
+      timing_type : hold_rising;
+      rise_constraint(hold_template_3x5) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "-0.09375, -0.0875, -0.075, -0.1125, -0.08125", \
+          "-0.0125, -0.00625, -0.0875, -0.03125, -0.09375", \
+          "-0.075, -0.06875, -0.05625, -0.09375, -0.0625");
+      }
+      fall_constraint(hold_template_3x5) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "-0.09375, -0.0875, -0.16875, -0.20625, -0.26875", \
+          "-0.2, -0.19375, -0.275, -0.3125, -0.375", \
+          "-0.2625, -0.25625, -0.3375, -0.375, -0.4375");
+      }
+    }
+    timing() {
+      related_pin : "CLK";
+      timing_type : setup_rising;
+      rise_constraint(setup_template_3x5) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.28125, 0.275, 0.35625, 0.3, 0.3625", \
+          "0.29375, 0.38125, 0.36875, 0.40625, 0.375", \
+          "0.45, 0.44375, 0.525, 0.46875, 0.53125");
+      }
+      fall_constraint(setup_template_3x5) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.28125, 0.275, 0.35625, 0.3, 0.45625", \
+          "0.29375, 0.2875, 0.36875, 0.40625, 0.46875", \
+          "0.45, 0.44375, 0.43125, 0.65625, 0.71875");
+      }
+    }
+  }
+  pin(Q)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.809569;
+    function : "DS0000";
+    timing() {
+      related_pin : "CLK";
+      timing_sense : non_unate;
+      timing_type : rising_edge;
+      cell_rise(delay_template_5x6) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.155157, 0.185043, 0.200943, 0.199225, 0.183875, 0.136501", \
+          "0.187733, 0.216666, 0.232876, 0.232403, 0.218026, 0.173249", \
+          "0.233455, 0.261446, 0.278507, 0.277236, 0.264079, 0.220551", \
+          "0.35604, 0.383739, 0.400674, 0.400086, 0.387318, 0.34478", \
+          "0.548234, 0.575647, 0.594155, 0.592191, 0.579607, 0.53882");
+      }
+      rise_transition(delay_template_5x6) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.0756, 0.0798, 0.0918, 0.108, 0.108, 0.1212", \
+          "0.1086, 0.1164, 0.1218, 0.1302, 0.1404, 0.1584", \
+          "0.1638, 0.168, 0.1728, 0.1824, 0.1896, 0.2046", \
+          "0.3276, 0.3282, 0.3324, 0.339, 0.3456, 0.3564", \
+          "0.6102, 0.6108, 0.6126, 0.6162, 0.6204, 0.63");
+      }
+      cell_fall(delay_template_5x6) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.252448, 0.268754, 0.287605, 0.305023, 0.310473, 0.312851", \
+          "0.287515, 0.303626, 0.320722, 0.338982, 0.34431, 0.346762", \
+          "0.335443, 0.351085, 0.367249, 0.385665, 0.392484, 0.394748", \
+          "0.452852, 0.468111, 0.484539, 0.502748, 0.509707, 0.511646", \
+          "0.629629, 0.644574, 0.661197, 0.678628, 0.686318, 0.689721");
+      }
+      fall_transition(delay_template_5x6) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.0864, 0.075, 0.075, 0.0756, 0.078, 0.078", \
+          "0.1098, 0.1104, 0.105, 0.105, 0.1044, 0.1062", \
+          "0.1536, 0.153, 0.1506, 0.153, 0.156, 0.1566", \
+          "0.2838, 0.2844, 0.2826, 0.2838, 0.2844, 0.285", \
+          "0.5106, 0.5106, 0.51, 0.5094, 0.5106, 0.5124");
+      }
+    }
+    internal_power() {
+      related_pin : "CLK";
+      rise_power(energy_template_5x6) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.218029, 0.333901, 0.540957, 0.908081, 1.17532, 1.69014", \
+          "0.209825, 0.322234, 0.512691, 0.877257, 1.13397, 1.64705", \
+          "0.204201, 0.312703, 0.493395, 0.85371, 1.10896, 1.61315", \
+          "0.201393, 0.302564, 0.479152, 0.820183, 1.07552, 1.57677", \
+          "0.197652, 0.297942, 0.467065, 0.800769, 1.04916, 1.55238");
+      }
+      fall_power(energy_template_5x6) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.316675, 0.400097, 0.535739, 0.809463, 1.01265, 1.41974", \
+          "0.302645, 0.37725, 0.521619, 0.793054, 0.996677, 1.4034", \
+          "0.290029, 0.369578, 0.514175, 0.783816, 0.984621, 1.38972", \
+          "0.28417, 0.363618, 0.505936, 0.777901, 0.978745, 1.38439", \
+          "0.281585, 0.360364, 0.502708, 0.775338, 0.976405, 1.37888");
+      }
+    }
+  }
+}
+
+/* -------------- *
+ * Design : DFFSR *
+ * -------------- */
+cell (DFFSR) {
+area : 704;
+  cell_leakage_power : 0.247939;
+  ff (P0002,P0003) {
+    next_state : "D";
+    clocked_on : "CLK";
+    clear : "(!R)";
+    preset : "(!S)";
+    clear_preset_var1 : L;
+  }
+  pin(CLK)  {
+    direction : input;
+    capacitance : 0.0134469;
+    rise_capacitance : 0.0133798;
+    fall_capacitance : 0.0134469;
+    clock : true;
+    internal_power() {
+      rise_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0.190997, 0.223621, 0.300287, 0.442835, 0.552417, 0.765449");
+      }
+      fall_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0.580077, 0.612991, 0.694086, 0.84286, 0.948037, 1.16752");
+      }
+    }
+    min_pulse_width_high : 0.393155;
+    min_pulse_width_low : 0.288592;
+  }
+  pin(D)  {
+    direction : input;
+    capacitance : 0.0135592;
+    rise_capacitance : 0.0134699;
+    fall_capacitance : 0.0135592;
+    internal_power() {
+      rise_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0.382749, 0.422566, 0.488682, 0.630591, 0.740418, 0.955367");
+      }
+      fall_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0.567677, 0.611799, 0.702822, 0.853421, 0.960626, 1.18171");
+      }
+    }
+    timing() {
+      related_pin : "CLK";
+      timing_type : hold_rising;
+      when : "S&R";
+      sdf_cond : "S\&R";
+      rise_constraint(hold_template_3x6) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0, -0.0375, -0.11875, -0.14375, -0.08125, -0.14375", \
+          "-0.0125, -0.05, -0.0375, -0.0625, -0.09375, -0.15625", \
+          "0.01875, -0.01875, -0.1, -0.125, -0.0625, -0.125");
+      }
+      fall_constraint(hold_template_3x6) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0, -0.0375, -0.025, -0.14375, -0.175, -0.2375", \
+          "0.08125, 0.04375, -0.0375, -0.0625, -0.1875, -0.15625", \
+          "0.1125, 0.075, -0.00625, -0.03125, -0.15625, -0.21875");
+      }
+    }
+    timing() {
+      related_pin : "CLK";
+      timing_type : setup_rising;
+      when : "S&R";
+      sdf_cond : "S\&R";
+      rise_constraint(setup_template_3x6) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.1875, 0.225, 0.2125, 0.2375, 0.26875, 0.33125", \
+          "0.10625, 0.14375, 0.225, 0.25, 0.28125, 0.25", \
+          "0.16875, 0.1125, 0.19375, 0.21875, 0.25, 0.21875");
+      }
+      fall_constraint(setup_template_3x6) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.09375, 0.225, 0.2125, 0.33125, 0.3625, 0.425", \
+          "0.10625, 0.14375, 0.225, 0.25, 0.28125, 0.34375", \
+          "0.2625, 0.20625, 0.38125, 0.3125, 0.34375, 0.40625");
+      }
+    }
+  }
+  pin(Q)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.40972;
+    function : "P0002";
+    timing() {
+      related_pin : "CLK";
+      timing_sense : non_unate;
+      timing_type : rising_edge;
+      cell_rise(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.518879, 0.553894, 0.583445, 0.615694, 0.634389, 0.651872", \
+          "1.29638, 1.33222, 1.36145, 1.39384, 1.41225, 1.42977", \
+          "2.6562, 2.69237, 2.72133, 2.75364, 2.77216, 2.78915", \
+          "6.15258, 6.18845, 6.21703, 6.24944, 6.26788, 6.28573", \
+          "8.09674, 8.13276, 8.16185, 8.19416, 8.21256, 8.23019", \
+          "10.0402, 10.0731, 10.1021, 10.1347, 10.1559, 10.1735");
+      }
+      rise_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.3126, 0.3132, 0.3132, 0.3126, 0.3132, 0.3126", \
+          "1.4754, 1.4748, 1.476, 1.476, 1.476, 1.4754", \
+          "3.5232, 3.5226, 3.5232, 3.5232, 3.5232, 3.5232", \
+          "8.79, 8.79, 8.7906, 8.7906, 8.79, 8.79", \
+          "11.7132, 11.7126, 11.7132, 11.7132, 11.7132, 11.7126", \
+          "14.6388, 14.643, 14.6424, 14.643, 14.6394, 14.6388");
+      }
+      cell_fall(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.519927, 0.556111, 0.58011, 0.611746, 0.621438, 0.62794", \
+          "1.20214, 1.23863, 1.26253, 1.29378, 1.30349, 1.31059", \
+          "2.39475, 2.43129, 2.45532, 2.48646, 2.49622, 2.50316", \
+          "5.46036, 5.49838, 5.52184, 5.55295, 5.56307, 5.56885", \
+          "7.16466, 7.2019, 7.22477, 7.25581, 7.26615, 7.27223", \
+          "8.86707, 8.90533, 8.92863, 8.95971, 8.96994, 8.97664");
+      }
+      fall_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.2544, 0.2544, 0.2526, 0.252, 0.2532, 0.2538", \
+          "1.1856, 1.1856, 1.1856, 1.1856, 1.1856, 1.1856", \
+          "2.8338, 2.8332, 2.8344, 2.8344, 2.8344, 2.8338", \
+          "7.0728, 7.0734, 7.0704, 7.0704, 7.071, 7.0728", \
+          "9.4266, 9.4278, 9.4248, 9.4242, 9.4248, 9.423", \
+          "11.7804, 11.7798, 11.781, 11.781, 11.781, 11.781");
+      }
+    }
+    timing() {
+      related_pin : "R";
+      timing_sense : positive_unate;
+      timing_type : clear;
+      cell_rise(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.321669, 0.321788, 0.322489, 0.311115, 0.293392, 0.249131", \
+          "1.0998, 1.09849, 1.10067, 1.09032, 1.07656, 1.04435", \
+          "2.46004, 2.45864, 2.46035, 2.44954, 2.43583, 2.40415", \
+          "5.95606, 5.95515, 5.95685, 5.94587, 5.93186, 5.89944", \
+          "7.89835, 7.8992, 7.90039, 7.88931, 7.87652, 7.84381", \
+          "9.84213, 9.84196, 9.84286, 9.83281, 9.8179, 9.78743");
+      }
+      rise_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.3108, 0.3102, 0.3132, 0.3228, 0.3306, 0.3426", \
+          "1.4754, 1.476, 1.4754, 1.4766, 1.4784, 1.4862", \
+          "3.5232, 3.5232, 3.5232, 3.5238, 3.5238, 3.5238", \
+          "8.7906, 8.7894, 8.7906, 8.7912, 8.7906, 8.7906", \
+          "11.7162, 11.7132, 11.715, 11.7162, 11.712, 11.712", \
+          "14.6418, 14.64, 14.6418, 14.6412, 14.6418, 14.6346");
+      }
+      cell_fall(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.341552, 0.401313, 0.477445, 0.593647, 0.671278, 0.807284", \
+          "1.02414, 1.08349, 1.15886, 1.27467, 1.35041, 1.48904", \
+          "2.21686, 2.27606, 2.35149, 2.46681, 2.54262, 2.68056", \
+          "5.28312, 5.34237, 5.41771, 5.53253, 5.60932, 5.74716", \
+          "6.98608, 7.0459, 7.12111, 7.23609, 7.31199, 7.45038", \
+          "8.68916, 8.74948, 8.82442, 8.94122, 9.01489, 9.154");
+      }
+      fall_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.2544, 0.255, 0.2559, 0.258, 0.2604, 0.2649", \
+          "1.185, 1.185, 1.1853, 1.1859, 1.1865, 1.1874", \
+          "2.8329, 2.8326, 2.8332, 2.8323, 2.8329, 2.8335", \
+          "7.0704, 7.0722, 7.0716, 7.071, 7.0713, 7.071", \
+          "9.4236, 9.4254, 9.4257, 9.4245, 9.4251, 9.4257", \
+          "11.7798, 11.7807, 11.7786, 11.781, 11.7786, 11.781");
+      }
+    }
+    timing() {
+      related_pin : "S";
+      timing_sense : negative_unate;
+      timing_type : preset;
+      cell_rise(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.448887, 0.497874, 0.554617, 0.644514, 0.701256, 0.808065", \
+          "1.22768, 1.27813, 1.3329, 1.42238, 1.48055, 1.58552", \
+          "2.58751, 2.63778, 2.69281, 2.7822, 2.84034, 2.94563", \
+          "6.08394, 6.1344, 6.18894, 6.27837, 6.33707, 6.44195", \
+          "8.02794, 8.07784, 8.13334, 8.2228, 8.28107, 8.38629", \
+          "9.97156, 10.0211, 10.0756, 10.166, 10.2233, 10.3279");
+      }
+      rise_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.3114, 0.3117, 0.312, 0.3123, 0.3126, 0.312", \
+          "1.4757, 1.4754, 1.476, 1.4757, 1.4751, 1.4748", \
+          "3.5232, 3.5232, 3.5229, 3.5229, 3.5229, 3.5229", \
+          "8.7903, 8.79, 8.79, 8.7906, 8.79, 8.79", \
+          "11.7141, 11.7141, 11.7132, 11.7132, 11.7132, 11.7126", \
+          "14.6394, 14.6406, 14.64, 14.6394, 14.6406, 14.6409");
+      }
+    }
+    internal_power() {
+      related_pin : "CLK";
+      rise_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.73592, 0.767062, 0.847522, 0.998322, 1.11965, 1.33817", \
+          "0.740712, 0.766766, 0.847568, 0.997792, 1.11847, 1.33816", \
+          "0.742001, 0.76771, 0.848199, 0.99926, 1.11864, 1.33904", \
+          "0.742234, 0.769158, 0.851447, 0.998907, 1.12025, 1.33929", \
+          "0.743252, 0.769001, 0.850027, 1.00164, 1.119, 1.3393", \
+          "0.743022, 0.769319, 0.849512, 1.00253, 1.12015, 1.33815");
+      }
+      fall_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.652746, 0.679211, 0.762324, 0.917066, 1.02414, 1.23933", \
+          "0.653612, 0.680163, 0.763623, 0.917016, 1.02384, 1.24061", \
+          "0.654181, 0.680476, 0.763811, 0.917453, 1.02408, 1.24124", \
+          "0.656278, 0.681096, 0.764012, 0.918748, 1.02496, 1.2418", \
+          "0.655477, 0.681019, 0.764226, 0.919351, 1.02519, 1.24235", \
+          "0.655734, 0.681445, 0.765133, 0.920448, 1.02507, 1.24272");
+      }
+    }
+    internal_power() {
+      related_pin : "R";
+      rise_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.20615, 0.290682, 0.456057, 0.779003, 1.01785, 1.50463", \
+          "0.207625, 0.289428, 0.454932, 0.77241, 1.00773, 1.48583", \
+          "0.208681, 0.289756, 0.455714, 0.772039, 1.00659, 1.48294", \
+          "0.211731, 0.290253, 0.457481, 0.771521, 1.00683, 1.48247", \
+          "0.211832, 0.288517, 0.456976, 0.770572, 1.00623, 1.48266", \
+          "0.209139, 0.288574, 0.457661, 0.770983, 1.00597, 1.48139");
+      }
+      fall_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.759664, 0.856842, 1.03816, 1.35291, 1.54871, 1.88746", \
+          "0.75968, 0.856286, 1.0364, 1.34526, 1.53472, 1.87098", \
+          "0.759784, 0.856702, 1.03644, 1.34488, 1.53358, 1.8692", \
+          "0.760096, 0.856528, 1.03619, 1.34576, 1.53368, 1.86998", \
+          "0.759313, 0.855817, 1.03631, 1.34498, 1.53304, 1.86983", \
+          "0.760394, 0.856357, 1.03605, 1.34518, 1.53299, 1.86998");
+      }
+    }
+    internal_power() {
+      related_pin : "S";
+      power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.764103, 0.832183, 0.96869, 1.23199, 1.41228, 1.74388", \
+          "0.762519, 0.831107, 0.968504, 1.2326, 1.41061, 1.74354", \
+          "0.76294, 0.831724, 0.969316, 1.23278, 1.41143, 1.74383", \
+          "0.763088, 0.832069, 0.970481, 1.23304, 1.41227, 1.74375", \
+          "0.763189, 0.831805, 0.970165, 1.23362, 1.41264, 1.74461", \
+          "0.762444, 0.831836, 0.970262, 1.23402, 1.41094, 1.74334");
+      }
+    }
+  }
+  pin(R)  {
+    direction : input;
+    capacitance : 0.0357813;
+    rise_capacitance : 0.0357813;
+    fall_capacitance : 0.0317563;
+    min_pulse_width_low : 0.192019;
+    timing() {
+      related_pin : "CLK";
+      timing_type : recovery_rising;
+      when : "D&S";
+      sdf_cond : "D\&S";
+      rise_constraint(recovery_template_3x6) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "-0.09375, -0.15, -0.06875, 0.05, 0.175, 0.33125", \
+          "-0.08125, -0.1375, -0.05625, 0.0625, 0.1875, 0.15625", \
+          "-0.20625, -0.16875, -0.18125, -0.0625, -0.03125, 0.21875");
+      }
+    }
+    timing() {
+      related_pin : "S";
+      timing_type : recovery_rising;
+      rise_constraint(recovery_template_6x6) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0, 0.05625, 0.06875, 0.04375, 0.10625, 0.184375", \
+          "0.0375, 0, 0.0125, 0.08125, 0.14375, 0.175", \
+          "0.025, 0.034375, 0, 0.06875, 0.13125, 0.1625", \
+          "0.003125, 0.0125, 0.025, 0.046875, 0.0625, 0.140625", \
+          "-0.0125, -0.003125, 0.009375, 0.03125, 0.09375, 0.125", \
+          "-0.04375, -0.034375, -0.021875, 0, 0.015625, 0.09375");
+      }
+    }
+    timing() {
+      related_pin : "CLK";
+      timing_type : removal_rising;
+      when : "D&S";
+      sdf_cond : "D\&S";
+      rise_constraint(removal_template_3x6) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.28125, 0.24375, 0.35, 0.41875, 0.3875, 0.5125", \
+          "0.26875, 0.325, 0.3375, 0.40625, 0.46875, 0.59375", \
+          "0.3, 0.35625, 0.36875, 0.4375, 0.5, 0.625");
+      }
+    }
+  }
+  pin(S)  {
+    direction : input;
+    capacitance : 0.0323255;
+    rise_capacitance : 0.021334;
+    fall_capacitance : 0.0323255;
+    internal_power() {
+      rise_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0.19773, 0.187624, 0.18974, 0.198762, 0.197079, 0.201817");
+      }
+      fall_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0, 0, 0, 0, 0, 0");
+      }
+    }
+    min_pulse_width_low : 0.255376;
+    timing() {
+      related_pin : "CLK";
+      timing_type : recovery_rising;
+      when : "!D&R";
+      sdf_cond : "\~D\&R";
+      rise_constraint(recovery_template_3x6) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0, 0.0375, 0.025, 0.05, 0.08125, 0.2375", \
+          "0.0125, -0.04375, 0.0375, 0.0625, 0.09375, 0.15625", \
+          "0.075, 0.01875, 0.00625, 0.40625, 0.15625, 0.40625");
+      }
+    }
+    timing() {
+      related_pin : "R";
+      timing_type : recovery_rising;
+      rise_constraint(recovery_template_6x6) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.09375, 0.05625, 0.06875, 0.090625, 0.10625, 0.1375", \
+          "0.0375, 0.09375, 0.059375, 0.08125, 0.096875, 0.128125", \
+          "0.071875, 0.08125, 0.09375, 0.06875, 0.084375, 0.115625", \
+          "0.05, 0.0125, 0.025, 0.046875, 0.0625, 0.09375", \
+          "0.034375, -0.003125, 0.009375, 0.03125, 0.046875, 0.078125", \
+          "-0.04375, -0.034375, -0.021875, -0.046875, 0.015625, 0.046875");
+      }
+    }
+    timing() {
+      related_pin : "CLK";
+      timing_type : removal_rising;
+      when : "!D&R";
+      sdf_cond : "\~D\&R";
+      rise_constraint(removal_template_3x6) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.09375, 0.15, 0.06875, 0.04375, 0.10625, 0.04375", \
+          "0.175, 0.1375, 0.15, 0.125, 0.09375, 0.125", \
+          "0.20625, 0.16875, 0.18125, 0.15625, 0.125, 0.15625");
+      }
+    }
+  }
+}
+
+/* ------------- *
+ * Design : FAX1 *
+ * ------------- */
+cell (FAX1) {
+area : 480;
+  cell_leakage_power : 0.162133;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.101702;
+    rise_capacitance : 0.100648;
+    fall_capacitance : 0.101702;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0912748;
+    rise_capacitance : 0.0912748;
+    fall_capacitance : 0.0860056;
+  }
+  pin(C)  {
+    direction : input;
+    capacitance : 0.064336;
+    rise_capacitance : 0.064336;
+    fall_capacitance : 0.064233;
+  }
+  pin(YC)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.405612;
+    function : "(((A B)+(B C))+(C A))";
+    timing() {
+      related_pin : "A";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.192937, 0.202293, 0.222417, 0.23603, 0.262001", \
+          "0.252032, 0.261902, 0.282854, 0.297212, 0.327113", \
+          "0.336688, 0.345538, 0.368228, 0.382804, 0.417572", \
+          "0.575869, 0.585084, 0.608065, 0.623073, 0.660902", \
+          "0.965229, 0.974557, 0.997456, 1.01213, 1.05025");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0816, 0.0804, 0.0879, 0.0939, 0.1068", \
+          "0.1503, 0.1476, 0.1527, 0.1587, 0.1722", \
+          "0.2637, 0.2625, 0.2661, 0.2682, 0.2817", \
+          "0.606, 0.606, 0.6069, 0.6087, 0.6159", \
+          "1.185, 1.185, 1.185, 1.1856, 1.1886");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.212316, 0.230368, 0.271993, 0.301851, 0.392584", \
+          "0.274262, 0.291144, 0.334346, 0.364751, 0.461587", \
+          "0.355791, 0.3728, 0.417563, 0.448945, 0.550937", \
+          "0.569312, 0.586364, 0.631171, 0.663795, 0.770166", \
+          "0.910578, 0.927654, 0.972381, 1.00471, 1.11225");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0756, 0.0777, 0.0837, 0.0891, 0.105", \
+          "0.1362, 0.1368, 0.1386, 0.1446, 0.162", \
+          "0.2247, 0.2247, 0.2292, 0.2319, 0.2487", \
+          "0.4911, 0.4911, 0.4917, 0.4947, 0.5031", \
+          "0.9528, 0.9528, 0.9528, 0.9534, 0.957");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.201655, 0.213928, 0.238544, 0.251321, 0.282216", \
+          "0.259034, 0.272741, 0.300326, 0.314485, 0.347352", \
+          "0.344335, 0.358175, 0.387437, 0.402523, 0.438405", \
+          "0.584089, 0.597845, 0.62806, 0.645074, 0.683716", \
+          "0.97336, 0.98699, 1.01703, 1.03436, 1.07543");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0831, 0.0819, 0.0876, 0.0897, 0.0969", \
+          "0.15, 0.1515, 0.1563, 0.1596, 0.1704", \
+          "0.2637, 0.264, 0.2691, 0.2715, 0.2832", \
+          "0.6066, 0.6066, 0.6081, 0.6102, 0.6186", \
+          "1.185, 1.185, 1.185, 1.1859, 1.1907");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.221213, 0.240731, 0.285449, 0.315389, 0.41356", \
+          "0.283246, 0.303871, 0.352007, 0.382508, 0.482071", \
+          "0.366584, 0.387835, 0.439518, 0.471404, 0.573799", \
+          "0.583619, 0.604617, 0.657663, 0.693503, 0.801186", \
+          "0.925474, 0.94676, 0.999626, 1.03604, 1.14957");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0786, 0.0768, 0.0831, 0.0906, 0.1008", \
+          "0.1389, 0.1404, 0.1467, 0.1485, 0.1608", \
+          "0.2298, 0.2289, 0.2367, 0.2424, 0.2532", \
+          "0.495, 0.4953, 0.4974, 0.5025, 0.5145", \
+          "0.9543, 0.9549, 0.9546, 0.9561, 0.966");
+      }
+    }
+    timing() {
+      related_pin : "C";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.185775, 0.210399, 0.245686, 0.266778, 0.312469", \
+          "0.243892, 0.27024, 0.307336, 0.328971, 0.375209", \
+          "0.329181, 0.355023, 0.394669, 0.415937, 0.465802", \
+          "0.568872, 0.594357, 0.633929, 0.655734, 0.706132", \
+          "0.957725, 0.98325, 1.0226, 1.04421, 1.09553");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0852, 0.0807, 0.0921, 0.0945, 0.1008", \
+          "0.1524, 0.1509, 0.1548, 0.1602, 0.1707", \
+          "0.2637, 0.2634, 0.2685, 0.2706, 0.2817", \
+          "0.606, 0.606, 0.606, 0.6081, 0.6135", \
+          "1.1841, 1.1844, 1.1841, 1.1847, 1.188");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.203317, 0.232392, 0.29518, 0.334702, 0.453856", \
+          "0.264792, 0.295214, 0.360803, 0.400337, 0.521061", \
+          "0.348261, 0.378841, 0.445571, 0.486885, 0.609869", \
+          "0.565673, 0.595953, 0.662919, 0.706274, 0.83131", \
+          "0.90777, 0.938204, 1.00512, 1.04815, 1.17621");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0792, 0.0765, 0.0843, 0.0912, 0.1044", \
+          "0.1365, 0.1401, 0.1476, 0.1494, 0.162", \
+          "0.2298, 0.2289, 0.2343, 0.2388, 0.2496", \
+          "0.4947, 0.495, 0.4965, 0.4998, 0.5079", \
+          "0.9543, 0.9546, 0.9546, 0.9552, 0.9612");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "-0.007976, -0.001582, 0.046036, 0.096632, 0.303378", \
+          "0.003901, 0.006036, 0.053579, 0.102898, 0.293237", \
+          "0.004187, 0.006911, 0.054089, 0.101705, 0.286621", \
+          "0.002743, 0.005395, 0.051254, 0.098395, 0.279569", \
+          "0.001821, 0.004696, 0.050175, 0.096875, 0.276158");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.751435, 0.756822, 0.815911, 0.870229, 1.07082", \
+          "0.753113, 0.756525, 0.814098, 0.868209, 1.05967", \
+          "0.752309, 0.755483, 0.812912, 0.865268, 1.05629", \
+          "0.751447, 0.754547, 0.811504, 0.863135, 1.04992", \
+          "0.751138, 0.754253, 0.811014, 0.862398, 1.04726");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.079095, 0.071988, 0.108511, 0.145461, 0.323354", \
+          "0.087668, 0.081948, 0.11915, 0.154305, 0.313515", \
+          "0.088238, 0.082519, 0.119066, 0.154328, 0.306697", \
+          "0.086954, 0.081108, 0.116745, 0.152259, 0.302454", \
+          "0.086343, 0.080371, 0.115854, 0.151083, 0.299904");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.697594, 0.691911, 0.738172, 0.776694, 0.94822", \
+          "0.698733, 0.693453, 0.736748, 0.780343, 0.935934", \
+          "0.69733, 0.692467, 0.734847, 0.777073, 0.936829", \
+          "0.694698, 0.690931, 0.732109, 0.774157, 0.931782", \
+          "0.694056, 0.690307, 0.731288, 0.773142, 0.929401");
+      }
+    }
+    internal_power() {
+      related_pin : "C";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.163673, 0.165668, 0.189463, 0.22829, 0.390871", \
+          "0.165439, 0.17055, 0.197339, 0.235655, 0.382059", \
+          "0.166138, 0.169989, 0.199144, 0.23583, 0.377468", \
+          "0.166578, 0.169587, 0.197806, 0.233484, 0.371962", \
+          "0.166896, 0.169673, 0.197451, 0.232882, 0.369249");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.583181, 0.587626, 0.631498, 0.669882, 0.82487", \
+          "0.585184, 0.588504, 0.631341, 0.669054, 0.811996", \
+          "0.585161, 0.586867, 0.628181, 0.667229, 0.810344", \
+          "0.582139, 0.584993, 0.625643, 0.66373, 0.805293", \
+          "0.581744, 0.584612, 0.625012, 0.662479, 0.802606");
+      }
+    }
+  }
+  pin(YS)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.409456;
+    function : "((A^B)^C)";
+    timing() {
+      related_pin : "A";
+      timing_sense : non_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.311797, 0.328706, 0.370858, 0.401209, 0.489974", \
+          "0.367147, 0.383417, 0.426146, 0.453648, 0.544812", \
+          "0.446564, 0.463136, 0.505936, 0.532612, 0.62307", \
+          "0.679909, 0.69597, 0.739454, 0.766625, 0.856786", \
+          "1.06837, 1.08444, 1.12769, 1.15482, 1.24479");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0762, 0.0753, 0.0768, 0.0777, 0.078", \
+          "0.1437, 0.1443, 0.1437, 0.1428, 0.1455", \
+          "0.2535, 0.2544, 0.2544, 0.2538, 0.2538", \
+          "0.5994, 0.5994, 0.5997, 0.5997, 0.6", \
+          "1.1832, 1.1832, 1.1829, 1.1835, 1.1832");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.299535, 0.309958, 0.329301, 0.341262, 0.363669", \
+          "0.357539, 0.367622, 0.38617, 0.399449, 0.421082", \
+          "0.434309, 0.445615, 0.464819, 0.476385, 0.49848", \
+          "0.644759, 0.656249, 0.67551, 0.68687, 0.70887", \
+          "0.985959, 0.997573, 1.01673, 1.02809, 1.04987");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0753, 0.0756, 0.0726, 0.0774, 0.0798", \
+          "0.1323, 0.1299, 0.1314, 0.1329, 0.1341", \
+          "0.2184, 0.2187, 0.2187, 0.2184, 0.2187", \
+          "0.4881, 0.4881, 0.4884, 0.4887, 0.4884", \
+          "0.9525, 0.9525, 0.9528, 0.9531, 0.9525");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : non_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.326354, 0.343646, 0.392103, 0.426361, 0.525078", \
+          "0.381898, 0.400236, 0.44672, 0.478563, 0.576663", \
+          "0.462937, 0.480605, 0.527821, 0.559818, 0.65523", \
+          "0.698543, 0.715548, 0.762286, 0.793601, 0.888746", \
+          "1.08749, 1.10456, 1.15106, 1.18221, 1.27704");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0774, 0.0786, 0.0801, 0.0822, 0.0807", \
+          "0.1482, 0.1467, 0.1479, 0.1461, 0.1464", \
+          "0.2568, 0.2568, 0.2571, 0.2553, 0.2547", \
+          "0.6012, 0.6012, 0.6012, 0.6006, 0.6003", \
+          "1.1838, 1.1841, 1.1841, 1.1838, 1.1838");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.311475, 0.321628, 0.34643, 0.359418, 0.388969", \
+          "0.368563, 0.378796, 0.403685, 0.415647, 0.443952", \
+          "0.446227, 0.456405, 0.482051, 0.493458, 0.521028", \
+          "0.656751, 0.667049, 0.692618, 0.704328, 0.731082", \
+          "0.998102, 1.00844, 1.03396, 1.04592, 1.07192");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0771, 0.0768, 0.075, 0.0765, 0.0768", \
+          "0.1341, 0.1341, 0.1326, 0.1314, 0.1314", \
+          "0.219, 0.2187, 0.2178, 0.2187, 0.2172", \
+          "0.4881, 0.4884, 0.4881, 0.4881, 0.4875", \
+          "0.9531, 0.9531, 0.9528, 0.9528, 0.9522");
+      }
+    }
+    timing() {
+      related_pin : "C";
+      timing_sense : non_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.322002, 0.346854, 0.408441, 0.447656, 0.560705", \
+          "0.376032, 0.403646, 0.463433, 0.50045, 0.613575", \
+          "0.455609, 0.483999, 0.542842, 0.580766, 0.69238", \
+          "0.690799, 0.71776, 0.777098, 0.815784, 0.926969", \
+          "1.07981, 1.10678, 1.16586, 1.20454, 1.31715");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0783, 0.0798, 0.0804, 0.0753, 0.0765", \
+          "0.1482, 0.1461, 0.1461, 0.1443, 0.1443", \
+          "0.2562, 0.2568, 0.2565, 0.2565, 0.255", \
+          "0.6012, 0.6012, 0.6018, 0.6021, 0.603", \
+          "1.1844, 1.1844, 1.1841, 1.1844, 1.1862");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.296791, 0.3165, 0.344383, 0.367168, 0.404984", \
+          "0.350874, 0.372846, 0.399754, 0.421069, 0.455738", \
+          "0.424904, 0.447341, 0.475681, 0.494426, 0.531328", \
+          "0.632276, 0.654496, 0.683205, 0.702501, 0.738492", \
+          "0.973424, 0.995389, 1.02414, 1.04363, 1.07937");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.072, 0.0708, 0.0678, 0.0759, 0.0699", \
+          "0.1242, 0.1236, 0.1236, 0.1236, 0.1218", \
+          "0.2118, 0.2118, 0.2118, 0.21, 0.2106", \
+          "0.4839, 0.4839, 0.4842, 0.4848, 0.4839", \
+          "0.9513, 0.9516, 0.9519, 0.9519, 0.9519");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.751435, 0.756822, 0.815911, 0.870229, 1.07082", \
+          "0.753113, 0.756525, 0.814098, 0.868209, 1.05967", \
+          "0.752309, 0.755483, 0.812912, 0.865268, 1.05629", \
+          "0.751447, 0.754547, 0.811504, 0.863135, 1.04992", \
+          "0.751138, 0.754253, 0.811014, 0.862398, 1.04726");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "-0.007976, -0.001582, 0.046036, 0.096632, 0.303378", \
+          "0.003901, 0.006036, 0.053579, 0.102898, 0.293237", \
+          "0.004187, 0.006911, 0.054089, 0.101705, 0.286621", \
+          "0.002743, 0.005395, 0.051254, 0.098395, 0.279569", \
+          "0.001821, 0.004696, 0.050175, 0.096875, 0.276158");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.697594, 0.691911, 0.738172, 0.776694, 0.94822", \
+          "0.698733, 0.693453, 0.736748, 0.780343, 0.935934", \
+          "0.69733, 0.692467, 0.734847, 0.777073, 0.936829", \
+          "0.694698, 0.690931, 0.732109, 0.774157, 0.931782", \
+          "0.694056, 0.690307, 0.731288, 0.773142, 0.929401");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.079095, 0.071988, 0.108511, 0.145461, 0.323354", \
+          "0.087668, 0.081948, 0.11915, 0.154305, 0.313515", \
+          "0.088238, 0.082519, 0.119066, 0.154328, 0.306697", \
+          "0.086954, 0.081108, 0.116745, 0.152259, 0.302454", \
+          "0.086343, 0.080371, 0.115854, 0.151083, 0.299904");
+      }
+    }
+    internal_power() {
+      related_pin : "C";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.583181, 0.587626, 0.631498, 0.669882, 0.82487", \
+          "0.585184, 0.588504, 0.631341, 0.669054, 0.811996", \
+          "0.585161, 0.586867, 0.628181, 0.667229, 0.810344", \
+          "0.582139, 0.584993, 0.625643, 0.66373, 0.805293", \
+          "0.581744, 0.584612, 0.625012, 0.662479, 0.802606");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.163673, 0.165668, 0.189463, 0.22829, 0.390871", \
+          "0.165439, 0.17055, 0.197339, 0.235655, 0.382059", \
+          "0.166138, 0.169989, 0.199144, 0.23583, 0.377468", \
+          "0.166578, 0.169587, 0.197806, 0.233484, 0.371962", \
+          "0.166896, 0.169673, 0.197451, 0.232882, 0.369249");
+      }
+    }
+  }
+}
+
+/* ------------- *
+ * Design : HAX1 *
+ * ------------- */
+cell (HAX1) {
+area : 320;
+  cell_leakage_power : 0.119043;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0388022;
+    rise_capacitance : 0.0386075;
+    fall_capacitance : 0.0388022;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0305266;
+    rise_capacitance : 0.0305266;
+    fall_capacitance : 0.0303638;
+  }
+  pin(YC)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.409798;
+    function : "(A B)";
+    timing() {
+      related_pin : "A";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.140081, 0.141507, 0.145815, 0.141975, 0.105465", \
+          "0.192758, 0.200282, 0.205017, 0.200982, 0.169417", \
+          "0.273446, 0.281692, 0.28655, 0.283745, 0.257017", \
+          "0.508344, 0.515827, 0.521407, 0.519244, 0.498649", \
+          "0.897028, 0.904153, 0.909429, 0.9075, 0.887575");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0762, 0.078, 0.0852, 0.0876, 0.1026", \
+          "0.1416, 0.1452, 0.1524, 0.1554, 0.1668", \
+          "0.2544, 0.2544, 0.2598, 0.2634, 0.2772", \
+          "0.5994, 0.6, 0.6012, 0.603, 0.6126", \
+          "1.1832, 1.1832, 1.1826, 1.1832, 1.1874");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.177776, 0.21539, 0.292039, 0.344016, 0.503526", \
+          "0.233812, 0.271739, 0.350776, 0.403476, 0.564403", \
+          "0.308649, 0.346346, 0.425841, 0.47909, 0.641612", \
+          "0.515371, 0.552913, 0.632453, 0.686181, 0.849006", \
+          "0.85623, 0.893748, 0.973013, 1.02703, 1.19026");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0708, 0.069, 0.0774, 0.0792, 0.0936", \
+          "0.123, 0.1272, 0.1296, 0.1332, 0.147", \
+          "0.2112, 0.2124, 0.2136, 0.2178, 0.2274", \
+          "0.483, 0.483, 0.4836, 0.4836, 0.4884", \
+          "0.951, 0.951, 0.951, 0.951, 0.9534");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.139715, 0.15581, 0.175213, 0.182298, 0.18117", \
+          "0.192261, 0.211131, 0.230293, 0.236731, 0.243006", \
+          "0.272931, 0.292124, 0.312354, 0.319517, 0.327988", \
+          "0.507829, 0.526401, 0.547907, 0.55753, 0.566311", \
+          "0.896495, 0.914845, 0.936268, 0.945993, 0.956004");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0762, 0.0762, 0.0852, 0.087, 0.102", \
+          "0.141, 0.1434, 0.1506, 0.1506, 0.1656", \
+          "0.2544, 0.2544, 0.2592, 0.2628, 0.2718", \
+          "0.5994, 0.5994, 0.6018, 0.6036, 0.609", \
+          "1.1832, 1.1832, 1.1832, 1.1838, 1.1874");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.160211, 0.196061, 0.260214, 0.304966, 0.432663", \
+          "0.214866, 0.250091, 0.317945, 0.36421, 0.49646", \
+          "0.288617, 0.324615, 0.393692, 0.440081, 0.575944", \
+          "0.495085, 0.531067, 0.601112, 0.647765, 0.78546", \
+          "0.835833, 0.871869, 0.941836, 0.988954, 1.12754");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0672, 0.0666, 0.0744, 0.0762, 0.093", \
+          "0.1242, 0.126, 0.1272, 0.1326, 0.147", \
+          "0.2082, 0.21, 0.213, 0.216, 0.2298", \
+          "0.4824, 0.483, 0.4836, 0.4854, 0.4902", \
+          "0.951, 0.951, 0.951, 0.951, 0.9534");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.160514, 0.158463, 0.199779, 0.237029, 0.37704", \
+          "0.160504, 0.164431, 0.197887, 0.235585, 0.367328", \
+          "0.160164, 0.163356, 0.195988, 0.232262, 0.363547", \
+          "0.160314, 0.162774, 0.194761, 0.230046, 0.358362", \
+          "0.160403, 0.162513, 0.1944, 0.227908, 0.356257");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.396875, 0.404062, 0.452447, 0.492602, 0.643019", \
+          "0.397423, 0.40069, 0.450249, 0.492511, 0.632396", \
+          "0.397506, 0.402443, 0.448683, 0.490893, 0.629695", \
+          "0.398133, 0.401943, 0.447108, 0.489025, 0.624878", \
+          "0.398197, 0.402345, 0.447218, 0.487214, 0.62227");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.224658, 0.22892, 0.261757, 0.297231, 0.433047", \
+          "0.225463, 0.230122, 0.257896, 0.290358, 0.423966", \
+          "0.2258, 0.22928, 0.257436, 0.29253, 0.419676", \
+          "0.226698, 0.22981, 0.258413, 0.291162, 0.415098", \
+          "0.227315, 0.229978, 0.258481, 0.289359, 0.413455");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.330044, 0.338208, 0.379681, 0.415719, 0.549072", \
+          "0.329278, 0.337061, 0.380366, 0.416585, 0.543336", \
+          "0.33099, 0.338175, 0.378926, 0.414532, 0.538716", \
+          "0.331315, 0.338473, 0.377401, 0.4134, 0.534897", \
+          "0.331856, 0.339051, 0.377815, 0.412232, 0.532665");
+      }
+    }
+  }
+  pin(YS)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.410646;
+    function : "(A^B)";
+    timing() {
+      related_pin : "A";
+      timing_sense : non_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.226483, 0.264825, 0.335188, 0.385169, 0.539641", \
+          "0.279458, 0.314273, 0.387161, 0.439429, 0.591255", \
+          "0.357283, 0.392988, 0.465122, 0.517341, 0.668378", \
+          "0.590066, 0.627756, 0.700682, 0.749613, 0.900129", \
+          "0.97829, 1.01614, 1.08881, 1.14012, 1.28984");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0744, 0.0726, 0.0732, 0.075, 0.0798", \
+          "0.1386, 0.1398, 0.1386, 0.1398, 0.1416", \
+          "0.2502, 0.2496, 0.2508, 0.2502, 0.2514", \
+          "0.5988, 0.5988, 0.5988, 0.5982, 0.5976", \
+          "1.1826, 1.1826, 1.1826, 1.1826, 1.1826");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.245071, 0.245767, 0.25396, 0.249775, 0.222849", \
+          "0.294656, 0.297167, 0.301037, 0.298483, 0.271082", \
+          "0.364286, 0.367589, 0.372678, 0.368259, 0.338349", \
+          "0.568746, 0.572204, 0.577076, 0.571975, 0.544754", \
+          "0.909275, 0.913048, 0.917501, 0.913574, 0.884601");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0696, 0.0654, 0.0642, 0.0684, 0.0648", \
+          "0.117, 0.1188, 0.1134, 0.1146, 0.1194", \
+          "0.204, 0.2028, 0.2034, 0.2028, 0.2046", \
+          "0.4812, 0.4812, 0.4806, 0.48, 0.4806", \
+          "0.9504, 0.9504, 0.9504, 0.9504, 0.9498");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : non_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.223106, 0.258079, 0.322791, 0.367532, 0.490477", \
+          "0.274891, 0.311397, 0.377288, 0.420411, 0.542851", \
+          "0.357884, 0.392727, 0.454702, 0.498672, 0.62117", \
+          "0.590898, 0.625574, 0.690412, 0.731576, 0.853157", \
+          "0.979674, 1.01443, 1.07902, 1.12177, 1.24272");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0774, 0.0762, 0.072, 0.075, 0.0792", \
+          "0.1428, 0.1422, 0.1368, 0.138, 0.141", \
+          "0.2514, 0.252, 0.2502, 0.2508, 0.2514", \
+          "0.5988, 0.5982, 0.5988, 0.5994, 0.5982", \
+          "1.1832, 1.1832, 1.1832, 1.1832, 1.1832");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.245491, 0.258393, 0.280463, 0.287808, 0.297282", \
+          "0.295235, 0.307083, 0.324249, 0.334572, 0.34247", \
+          "0.365064, 0.377362, 0.393697, 0.402759, 0.413535", \
+          "0.569651, 0.581639, 0.599156, 0.608359, 0.619514", \
+          "0.910265, 0.922458, 0.940059, 0.950117, 0.958697");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0696, 0.063, 0.0642, 0.0678, 0.0648", \
+          "0.117, 0.1194, 0.114, 0.1194, 0.1182", \
+          "0.204, 0.2028, 0.2016, 0.2016, 0.2064", \
+          "0.4812, 0.4812, 0.4806, 0.48, 0.4806", \
+          "0.951, 0.951, 0.9504, 0.9504, 0.9486");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.396875, 0.404062, 0.452447, 0.492602, 0.643019", \
+          "0.397423, 0.40069, 0.450249, 0.492511, 0.632396", \
+          "0.397506, 0.402443, 0.448683, 0.490893, 0.629695", \
+          "0.398133, 0.401943, 0.447108, 0.489025, 0.624878", \
+          "0.398197, 0.402345, 0.447218, 0.487214, 0.62227");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.160514, 0.158463, 0.199779, 0.237029, 0.37704", \
+          "0.160504, 0.164431, 0.197887, 0.235585, 0.367328", \
+          "0.160164, 0.163356, 0.195988, 0.232262, 0.363547", \
+          "0.160314, 0.162774, 0.194761, 0.230046, 0.358362", \
+          "0.160403, 0.162513, 0.1944, 0.227908, 0.356257");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.330044, 0.338208, 0.379681, 0.415719, 0.549072", \
+          "0.329278, 0.337061, 0.380366, 0.416585, 0.543336", \
+          "0.33099, 0.338175, 0.378926, 0.414532, 0.538716", \
+          "0.331315, 0.338473, 0.377401, 0.4134, 0.534897", \
+          "0.331856, 0.339051, 0.377815, 0.412232, 0.532665");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.224658, 0.22892, 0.261757, 0.297231, 0.433047", \
+          "0.225463, 0.230122, 0.257896, 0.290358, 0.423966", \
+          "0.2258, 0.22928, 0.257436, 0.29253, 0.419676", \
+          "0.226698, 0.22981, 0.258413, 0.291162, 0.415098", \
+          "0.227315, 0.229978, 0.258481, 0.289359, 0.413455");
+      }
+    }
+  }
+}
+
+/* -------------- *
+ * Design : INVX1 *
+ * -------------- */
+cell (INVX1) {
+  cell_footprint : inv;
+area : 64;
+  cell_leakage_power : 0.0152465;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0134094;
+    rise_capacitance : 0.0133816;
+    fall_capacitance : 0.0134094;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.411688;
+    function : "(!A)";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.052639, 0.068333, 0.081478, 0.085569, 0.084748", \
+          "0.097195, 0.126587, 0.16039, 0.17465, 0.201755", \
+          "0.165859, 0.196317, 0.252434, 0.279356, 0.337341", \
+          "0.370193, 0.400018, 0.462928, 0.51244, 0.636385", \
+          "0.711823, 0.740267, 0.800617, 0.847739, 1.01201");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.052483, 0.081, 0.1224, 0.147, 0.2352", \
+          "0.102, 0.126, 0.1776, 0.2118, 0.3198", \
+          "0.198, 0.2016, 0.2544, 0.2964, 0.4182", \
+          "0.4782, 0.4782, 0.4926, 0.5226, 0.6624", \
+          "0.9486, 0.9486, 0.9486, 0.9534, 1.0398");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.058149, 0.090142, 0.128455, 0.154985, 0.223129", \
+          "0.108058, 0.145152, 0.205349, 0.241092, 0.338706", \
+          "0.186156, 0.222784, 0.298417, 0.345512, 0.472922", \
+          "0.418848, 0.453345, 0.529245, 0.588239, 0.771661", \
+          "0.8072, 0.841235, 0.913651, 0.969975, 1.16561");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.063, 0.0918, 0.129, 0.1566, 0.2322", \
+          "0.1314, 0.1458, 0.1926, 0.2262, 0.3234", \
+          "0.2472, 0.2508, 0.2874, 0.3252, 0.4368", \
+          "0.597, 0.5964, 0.6042, 0.624, 0.7302", \
+          "1.1808, 1.1814, 1.1814, 1.1826, 1.236");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.049646, 0.035086, 0.026031, 0.078193, 0.265592", \
+          "0.048186, 0.0394, 0.00589, 0.049663, 0.218626", \
+          "0.046864, 0.041207, 0.008276, 0.026567, 0.17172", \
+          "0.04576, 0.042684, 0.023921, 0.001348, 0.103465", \
+          "0.0448, 0.043548, 0.032279, 0.017652, 0.056285");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.119298, 0.145859, 0.211551, 0.266374, 0.457786", \
+          "0.122167, 0.136701, 0.19091, 0.239316, 0.417647", \
+          "0.123064, 0.133133, 0.174199, 0.215185, 0.375367", \
+          "0.124147, 0.128553, 0.153734, 0.182116, 0.306417", \
+          "0.124642, 0.126623, 0.142218, 0.161175, 0.252866");
+      }
+    }
+  }
+}
+
+/* -------------- *
+ * Design : INVX2 *
+ * -------------- */
+cell (INVX2) {
+  cell_footprint : inv;
+area : 64;
+  cell_leakage_power : 0.0157446;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0274396;
+    rise_capacitance : 0.0273864;
+    fall_capacitance : 0.0274396;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.833389;
+    function : "(!A)";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.053273, 0.06978, 0.084273, 0.089866, 0.093537", \
+          "0.098224, 0.127946, 0.162893, 0.178321, 0.209513", \
+          "0.167714, 0.1981, 0.255068, 0.282839, 0.344741", \
+          "0.374102, 0.403842, 0.466889, 0.516504, 0.643132", \
+          "0.719218, 0.747557, 0.807921, 0.854974, 1.01972");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.05309, 0.081, 0.1224, 0.1458, 0.234", \
+          "0.1038, 0.1266, 0.1782, 0.213, 0.3204", \
+          "0.2004, 0.2046, 0.2556, 0.2976, 0.4182", \
+          "0.4836, 0.4842, 0.498, 0.5274, 0.6642", \
+          "0.96, 0.96, 0.96, 0.9642, 1.0488");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.057563, 0.088297, 0.124843, 0.148937, 0.213231", \
+          "0.106557, 0.143139, 0.200715, 0.235009, 0.327528", \
+          "0.183803, 0.21972, 0.293462, 0.338716, 0.461012", \
+          "0.412816, 0.446995, 0.521494, 0.579617, 0.757738", \
+          "0.795462, 0.828949, 0.900107, 0.95544, 1.14805");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0624, 0.0912, 0.1284, 0.1548, 0.2304", \
+          "0.1302, 0.1458, 0.1926, 0.2256, 0.3228", \
+          "0.2442, 0.2478, 0.2862, 0.324, 0.4344", \
+          "0.5898, 0.5898, 0.5976, 0.6186, 0.7266", \
+          "1.167, 1.1676, 1.167, 1.1682, 1.2246");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.101724, 0.071701, 0.051374, 0.157485, 0.537212", \
+          "0.098082, 0.080785, 0.011251, 0.099968, 0.442596", \
+          "0.095359, 0.083957, 0.017501, 0.053193, 0.347889", \
+          "0.09303, 0.086987, 0.049073, 0.003257, 0.209651", \
+          "0.091167, 0.088673, 0.065853, 0.03622, 0.114007");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.243651, 0.297099, 0.429621, 0.541015, 0.928771", \
+          "0.249269, 0.278856, 0.388479, 0.486386, 0.8477", \
+          "0.251342, 0.27161, 0.354766, 0.437759, 0.762223", \
+          "0.253549, 0.262561, 0.313436, 0.370899, 0.622707", \
+          "0.254513, 0.258642, 0.290179, 0.328649, 0.514457");
+      }
+    }
+  }
+}
+
+/* -------------- *
+ * Design : INVX4 *
+ * -------------- */
+cell (INVX4) {
+  cell_footprint : inv;
+area : 96;
+  cell_leakage_power : 0.0314893;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0548793;
+    rise_capacitance : 0.0547729;
+    fall_capacitance : 0.0548793;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 1.66678;
+    function : "(!A)";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.06, 0.16, 0.32, 0.8, 1.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.053273, 0.06978, 0.084273, 0.089866, 0.093537", \
+          "0.098224, 0.127946, 0.162893, 0.178321, 0.209513", \
+          "0.167714, 0.1981, 0.255068, 0.282839, 0.344741", \
+          "0.374102, 0.403842, 0.466889, 0.516504, 0.643132", \
+          "0.719218, 0.747557, 0.807921, 0.854974, 1.01972");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.06, 0.16, 0.32, 0.8, 1.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.05309, 0.081, 0.1224, 0.1458, 0.234", \
+          "0.1038, 0.1266, 0.1782, 0.213, 0.3204", \
+          "0.2004, 0.2046, 0.2556, 0.2976, 0.4182", \
+          "0.4836, 0.4842, 0.498, 0.5274, 0.6642", \
+          "0.96, 0.96, 0.96, 0.9642, 1.0488");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.06, 0.16, 0.32, 0.8, 1.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.057563, 0.088297, 0.124843, 0.148937, 0.213231", \
+          "0.106557, 0.143139, 0.200715, 0.235009, 0.327528", \
+          "0.183803, 0.21972, 0.293462, 0.338716, 0.461012", \
+          "0.412816, 0.446995, 0.521494, 0.579617, 0.757738", \
+          "0.795462, 0.828949, 0.900107, 0.95544, 1.14805");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.06, 0.16, 0.32, 0.8, 1.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0624, 0.0912, 0.1284, 0.1548, 0.2304", \
+          "0.1302, 0.1458, 0.1926, 0.2256, 0.3228", \
+          "0.2442, 0.2478, 0.2862, 0.324, 0.4344", \
+          "0.5898, 0.5898, 0.5976, 0.6186, 0.7266", \
+          "1.167, 1.1676, 1.167, 1.1682, 1.2246");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.06, 0.16, 0.32, 0.8, 1.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.203448, 0.143402, 0.102748, 0.314969, 1.07442", \
+          "0.196163, 0.16157, 0.022503, 0.199936, 0.885191", \
+          "0.190719, 0.167915, 0.035001, 0.106387, 0.695778", \
+          "0.186059, 0.173974, 0.098145, 0.006513, 0.419303", \
+          "0.182333, 0.177347, 0.131707, 0.07244, 0.228013");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.06, 0.16, 0.32, 0.8, 1.6");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.487301, 0.594198, 0.859243, 1.08203, 1.85754", \
+          "0.498537, 0.557713, 0.776959, 0.972772, 1.6954", \
+          "0.502684, 0.54322, 0.709532, 0.875517, 1.52445", \
+          "0.507097, 0.525122, 0.626871, 0.741798, 1.24541", \
+          "0.509027, 0.517285, 0.580358, 0.657299, 1.02891");
+      }
+    }
+  }
+}
+
+/* -------------- *
+ * Design : INVX8 *
+ * -------------- */
+cell (INVX8) {
+  cell_footprint : inv;
+area : 160;
+  cell_leakage_power : 0.0629795;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.109759;
+    rise_capacitance : 0.109546;
+    fall_capacitance : 0.109759;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 3.33356;
+    function : "(!A)";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.12, 0.32, 0.64, 1.6, 3.2");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.053273, 0.06978, 0.084273, 0.089866, 0.093537", \
+          "0.098224, 0.127946, 0.162893, 0.178321, 0.209513", \
+          "0.167714, 0.1981, 0.255068, 0.282839, 0.344741", \
+          "0.374102, 0.403842, 0.466889, 0.516504, 0.643132", \
+          "0.719218, 0.747557, 0.807921, 0.854974, 1.01972");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.12, 0.32, 0.64, 1.6, 3.2");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.05309, 0.081, 0.1224, 0.1458, 0.234", \
+          "0.1038, 0.1266, 0.1782, 0.213, 0.3204", \
+          "0.2004, 0.2046, 0.2556, 0.2976, 0.4182", \
+          "0.4836, 0.4842, 0.498, 0.5274, 0.6642", \
+          "0.96, 0.96, 0.96, 0.9642, 1.0488");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.12, 0.32, 0.64, 1.6, 3.2");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.057563, 0.088297, 0.124843, 0.148937, 0.213231", \
+          "0.106557, 0.143139, 0.200715, 0.235009, 0.327528", \
+          "0.183803, 0.21972, 0.293462, 0.338716, 0.461012", \
+          "0.412816, 0.446995, 0.521494, 0.579617, 0.757738", \
+          "0.795462, 0.828949, 0.900107, 0.95544, 1.14805");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.12, 0.32, 0.64, 1.6, 3.2");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0624, 0.0912, 0.1284, 0.1548, 0.2304", \
+          "0.1302, 0.1458, 0.1926, 0.2256, 0.3228", \
+          "0.2442, 0.2478, 0.2862, 0.324, 0.4344", \
+          "0.5898, 0.5898, 0.5976, 0.6186, 0.7266", \
+          "1.167, 1.1676, 1.167, 1.1682, 1.2246");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.12, 0.32, 0.64, 1.6, 3.2");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.406897, 0.286803, 0.205496, 0.629939, 2.14885", \
+          "0.392327, 0.32314, 0.045005, 0.399872, 1.77038", \
+          "0.381437, 0.33583, 0.070003, 0.212774, 1.39156", \
+          "0.372119, 0.347949, 0.196291, 0.013026, 0.838605", \
+          "0.364666, 0.354693, 0.263414, 0.14488, 0.456026");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.12, 0.32, 0.64, 1.6, 3.2");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.974603, 1.1884, 1.71849, 2.16406, 3.71509", \
+          "0.997075, 1.11543, 1.55392, 1.94554, 3.3908", \
+          "1.00537, 1.08644, 1.41906, 1.75103, 3.04889", \
+          "1.01419, 1.05024, 1.25374, 1.4836, 2.49083", \
+          "1.01805, 1.03457, 1.16071, 1.3146, 2.05783");
+      }
+    }
+  }
+}
+
+/* -------------- *
+ * Design : LATCH *
+ * -------------- */
+cell (LATCH) {
+area : 0;
+  cell_leakage_power : 0.0663071;
+  latch (DS0000,P0000) {
+    data_in : "D";
+    enable : "CLK";
+  }
+  pin(CLK)  {
+    direction : input;
+    capacitance : 0.0323725;
+    rise_capacitance : 0.0323724;
+    fall_capacitance : 0.0323725;
+    clock : true;
+    internal_power() {
+      rise_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0.059791, 0.025688, 0.175257, 0.444134, 0.646377, 1.04931");
+      }
+      fall_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0.291395, 0.407135, 0.555759, 0.828325, 1.03184, 1.42916");
+      }
+    }
+    min_pulse_width_high : 0.211198;
+  }
+  pin(D)  {
+    direction : input;
+    capacitance : 0.0130297;
+    rise_capacitance : 0.0129486;
+    fall_capacitance : 0.0130297;
+    timing() {
+      related_pin : "CLK";
+      timing_type : hold_falling;
+      rise_constraint(hold_template_3x6) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "-0.09375, -0.13125, -0.11875, -0.14375, -0.175, -0.2375", \
+          "-0.2, -0.2375, -0.225, -0.25, -0.28125, -0.25", \
+          "-0.2625, -0.20625, -0.2875, -0.3125, -0.25, -0.3125");
+      }
+      fall_constraint(hold_template_3x6) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "-0.09375, -0.13125, -0.2125, -0.2375, -0.3625, -0.425001", \
+          "-0.10625, -0.14375, -0.225, -0.25, -0.28125, -0.4375", \
+          "-0.075, -0.1125, -0.19375, -0.21875, -0.25, -0.40625");
+      }
+    }
+    timing() {
+      related_pin : "CLK";
+      timing_type : setup_falling;
+      rise_constraint(setup_template_3x6) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.28125, 0.31875, 0.30625, 5.3, 5.05, 0.425", \
+          "0.3875, 0.33125, 0.4125, 5.5, 5.25, 0.4375", \
+          "0.54375, 0.58125, 0.56875, 5.75, 5.5, 0.59375");
+      }
+      fall_constraint(setup_template_3x6) {
+        index_1 ("0.06, 0.3, 0.6");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.28125, 0.31875, 0.4, 0.425, 0.55, 0.6125", \
+          "0.29375, 0.33125, 0.31875, 0.4375, 0.46875, 0.625", \
+          "0.2625, 0.3, 0.2875, 0.40625, 0.4375, 0.59375");
+      }
+    }
+  }
+  pin(Q)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.818968;
+    function : "DS0000";
+    timing() {
+      related_pin : "CLK";
+      timing_sense : non_unate;
+      timing_type : rising_edge;
+      cell_rise(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.25464, 0.2835, 0.298283, 0.296756, 0.284761, 0.240906", \
+          "0.643501, 0.671746, 0.687301, 0.686064, 0.673915, 0.634005", \
+          "1.31317, 1.34122, 1.35641, 1.35465, 1.3425, 1.30297", \
+          "3.03589, 3.06385, 3.07825, 3.07582, 3.06349, 3.0233", \
+          "3.99267, 4.01974, 4.03514, 4.03266, 4.02008, 3.97966", \
+          "4.94867, 4.97632, 4.99091, 4.98907, 4.97723, 4.93703");
+      }
+      rise_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.1902, 0.1938, 0.198, 0.2094, 0.2154, 0.2292", \
+          "0.7536, 0.7536, 0.7548, 0.759, 0.762, 0.7704", \
+          "1.764, 1.7646, 1.7634, 1.7646, 1.7664, 1.7706", \
+          "4.3644, 4.3644, 4.3644, 4.3638, 4.3644, 4.3656", \
+          "5.8098, 5.8098, 5.8092, 5.8092, 5.8086, 5.8086", \
+          "7.254, 7.2546, 7.254, 7.2516, 7.254, 7.2534");
+      }
+      cell_fall(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.34292, 0.353051, 0.363153, 0.373403, 0.372831, 0.368803", \
+          "0.702397, 0.712669, 0.72171, 0.731291, 0.732274, 0.730637", \
+          "1.30518, 1.31549, 1.32428, 1.33377, 1.334, 1.33334", \
+          "2.85453, 2.86442, 2.87348, 2.88288, 2.88302, 2.88211", \
+          "3.71469, 3.72488, 3.73381, 3.74351, 3.74339, 3.74315", \
+          "4.57506, 4.5854, 4.59471, 4.60389, 4.60393, 4.60374");
+      }
+      fall_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.1758, 0.1746, 0.1722, 0.177, 0.1728, 0.1764", \
+          "0.627, 0.6264, 0.627, 0.6264, 0.627, 0.6294", \
+          "1.455, 1.455, 1.4556, 1.455, 1.455, 1.455", \
+          "3.6, 3.6, 3.5988, 3.6, 3.6, 3.597", \
+          "4.7916, 4.7916, 4.7916, 4.791, 4.791, 4.7904", \
+          "5.982, 5.9814, 5.9832, 5.9832, 5.9826, 5.9826");
+      }
+    }
+    timing() {
+      related_pin : "D";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.300641, 0.31834, 0.340922, 0.369343, 0.382949, 0.398324", \
+          "0.689617, 0.706549, 0.729, 0.758451, 0.775012, 0.80033", \
+          "1.35926, 1.3759, 1.39862, 1.4273, 1.44395, 1.4694", \
+          "3.08177, 3.09825, 3.12049, 3.1489, 3.16546, 3.1899", \
+          "4.03799, 4.05438, 4.07784, 4.10619, 4.12184, 4.14697", \
+          "4.99553, 5.01232, 5.03452, 5.06177, 5.07893, 5.1039");
+      }
+      rise_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.192, 0.1932, 0.195, 0.2052, 0.2118, 0.2268", \
+          "0.7536, 0.7536, 0.7548, 0.7572, 0.7608, 0.7722", \
+          "1.764, 1.7646, 1.7634, 1.7652, 1.7652, 1.7694", \
+          "4.3644, 4.3644, 4.3644, 4.3644, 4.3644, 4.365", \
+          "5.8098, 5.8098, 5.8074, 5.8092, 5.8092, 5.8086", \
+          "7.254, 7.2516, 7.2516, 7.2546, 7.2528, 7.2534");
+      }
+      cell_fall(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.339721, 0.368889, 0.413603, 0.491982, 0.543438, 0.637333", \
+          "0.699252, 0.727242, 0.77337, 0.854433, 0.909488, 1.01496", \
+          "1.30198, 1.32974, 1.37567, 1.45604, 1.51127, 1.61737", \
+          "2.85088, 2.87853, 2.92432, 3.00443, 3.05907, 3.16397", \
+          "3.71135, 3.73927, 3.7846, 3.86474, 3.91943, 4.02378", \
+          "4.57185, 4.59953, 4.64532, 4.72577, 4.78023, 4.88453");
+      }
+      fall_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.1752, 0.1776, 0.1806, 0.192, 0.1998, 0.2172", \
+          "0.6264, 0.627, 0.6282, 0.6312, 0.6354, 0.648", \
+          "1.455, 1.455, 1.455, 1.4562, 1.4574, 1.461", \
+          "3.6, 3.5994, 3.5994, 3.5982, 3.5982, 3.5994", \
+          "4.7916, 4.7916, 4.7916, 4.7892, 4.7904, 4.7898", \
+          "5.982, 5.9808, 5.9808, 5.9826, 5.9832, 5.9826");
+      }
+    }
+    internal_power() {
+      related_pin : "CLK";
+      rise_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.190029, 0.305976, 0.492549, 0.854761, 1.11302, 1.61867", \
+          "0.185583, 0.289465, 0.468692, 0.806324, 1.05352, 1.55671", \
+          "0.184676, 0.286908, 0.46158, 0.794558, 1.03749, 1.52959", \
+          "0.183352, 0.285398, 0.45868, 0.789028, 1.02919, 1.51625", \
+          "0.18321, 0.28508, 0.457956, 0.787972, 1.02765, 1.51377", \
+          "0.182915, 0.284624, 0.457563, 0.787111, 1.02673, 1.51217");
+      }
+      fall_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.287213, 0.368803, 0.519199, 0.799488, 0.996622, 1.40398", \
+          "0.280584, 0.361752, 0.513087, 0.789597, 0.988926, 1.39659", \
+          "0.279699, 0.360527, 0.512231, 0.787954, 0.988822, 1.39557", \
+          "0.279358, 0.360466, 0.51216, 0.787522, 0.988785, 1.39539", \
+          "0.279131, 0.360447, 0.512292, 0.787568, 0.98868, 1.39552", \
+          "0.279119, 0.360267, 0.512126, 0.787376, 0.988563, 1.39555");
+      }
+    }
+    internal_power() {
+      related_pin : "D";
+      rise_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.259848, 0.280556, 0.335552, 0.464372, 0.566057, 0.777638", \
+          "0.249441, 0.267287, 0.313721, 0.424518, 0.512153, 0.697198", \
+          "0.246869, 0.263503, 0.30924, 0.414796, 0.497609, 0.672024", \
+          "0.245701, 0.261794, 0.307041, 0.409745, 0.48997, 0.658745", \
+          "0.245338, 0.261378, 0.306469, 0.408779, 0.488668, 0.656337", \
+          "0.245052, 0.261023, 0.306059, 0.407953, 0.487684, 0.654768");
+      }
+      fall_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.494134, 0.514952, 0.569224, 0.699783, 0.800885, 1.01195", \
+          "0.486881, 0.502107, 0.552319, 0.664401, 0.753247, 0.938851", \
+          "0.485371, 0.499685, 0.548709, 0.657185, 0.741676, 0.918246", \
+          "0.484307, 0.498995, 0.54689, 0.653587, 0.736264, 0.908222", \
+          "0.484301, 0.498814, 0.54661, 0.652877, 0.735419, 0.906522", \
+          "0.484004, 0.498865, 0.546297, 0.652729, 0.734997, 0.905378");
+      }
+    }
+  }
+}
+
+/* --------------- *
+ * Design : MUX2X1 *
+ * --------------- */
+cell (MUX2X1) {
+area : 192;
+  cell_leakage_power : 0.0613993;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0265929;
+    rise_capacitance : 0.0263548;
+    fall_capacitance : 0.0265929;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0265339;
+    rise_capacitance : 0.0263422;
+    fall_capacitance : 0.0265339;
+  }
+  pin(S)  {
+    direction : input;
+    capacitance : 0.0295792;
+    rise_capacitance : 0.0295792;
+    fall_capacitance : 0.029555;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.411779;
+    function : "(!((S A) + (!S B)))";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.082336, 0.089775, 0.10243, 0.105517, 0.104045", \
+          "0.121841, 0.133302, 0.149609, 0.157831, 0.169164", \
+          "0.183321, 0.193781, 0.215459, 0.228459, 0.255794", \
+          "0.364819, 0.37103, 0.393957, 0.413017, 0.465415", \
+          "0.657797, 0.663229, 0.683063, 0.700999, 0.767603");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0936, 0.108, 0.156, 0.1944, 0.3072", \
+          "0.1452, 0.159, 0.1992, 0.2322, 0.3564", \
+          "0.2268, 0.2334, 0.2646, 0.297, 0.4212", \
+          "0.477, 0.477, 0.4902, 0.5106, 0.6138", \
+          "0.8958, 0.8958, 0.8976, 0.906, 0.9678");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.106454, 0.123537, 0.153215, 0.175541, 0.244758", \
+          "0.157152, 0.170526, 0.20586, 0.232608, 0.31443", \
+          "0.229941, 0.243556, 0.281655, 0.312069, 0.40731", \
+          "0.450496, 0.462126, 0.498543, 0.531292, 0.640031", \
+          "0.81666, 0.827512, 0.859418, 0.887589, 0.998108");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1086, 0.1254, 0.1668, 0.201, 0.3144", \
+          "0.1788, 0.189, 0.2244, 0.2568, 0.3714", \
+          "0.2874, 0.2916, 0.3168, 0.3462, 0.4554", \
+          "0.6174, 0.6174, 0.6264, 0.6402, 0.7218", \
+          "1.1688, 1.1682, 1.1682, 1.1736, 1.2138");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.093802, 0.098897, 0.105613, 0.108217, 0.105219", \
+          "0.131752, 0.139637, 0.153538, 0.160935, 0.17077", \
+          "0.192565, 0.199873, 0.219392, 0.231947, 0.257536", \
+          "0.368648, 0.376833, 0.398401, 0.416656, 0.46774", \
+          "0.661744, 0.668804, 0.687294, 0.705066, 0.77052");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0918, 0.1092, 0.1506, 0.1896, 0.303", \
+          "0.144, 0.1566, 0.1968, 0.2298, 0.354", \
+          "0.2262, 0.234, 0.264, 0.297, 0.42", \
+          "0.4788, 0.4782, 0.492, 0.5124, 0.6144", \
+          "0.8982, 0.8982, 0.9006, 0.909, 0.9702");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.096297, 0.115488, 0.149039, 0.173485, 0.243713", \
+          "0.146697, 0.164524, 0.202557, 0.229598, 0.312694", \
+          "0.219348, 0.23835, 0.277782, 0.309044, 0.40514", \
+          "0.44378, 0.456353, 0.494291, 0.527464, 0.637409", \
+          "0.810029, 0.823437, 0.854837, 0.883393, 0.994754");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1104, 0.1296, 0.174, 0.204, 0.3162", \
+          "0.1794, 0.1884, 0.228, 0.258, 0.3726", \
+          "0.2868, 0.2898, 0.3168, 0.3462, 0.4554", \
+          "0.615, 0.6156, 0.624, 0.6378, 0.7194", \
+          "1.1658, 1.1658, 1.1658, 1.1706, 1.2108");
+      }
+    }
+    timing() {
+      related_pin : "S";
+      timing_sense : non_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.152242, 0.167035, 0.202409, 0.215544, 0.241432", \
+          "0.197231, 0.215666, 0.248914, 0.264347, 0.291801", \
+          "0.266861, 0.287321, 0.317916, 0.335817, 0.366422", \
+          "0.481234, 0.505295, 0.5338, 0.546357, 0.582994", \
+          "0.844084, 0.868446, 0.896951, 0.909845, 0.939374");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1098, 0.1104, 0.108, 0.1116, 0.1146", \
+          "0.1806, 0.1806, 0.1686, 0.174, 0.1806", \
+          "0.2874, 0.2886, 0.282, 0.276, 0.2832", \
+          "0.6168, 0.6174, 0.6138, 0.6102, 0.6066", \
+          "1.1682, 1.1688, 1.1682, 1.1658, 1.1592");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.159088, 0.192223, 0.252428, 0.289023, 0.384499", \
+          "0.19869, 0.228037, 0.291557, 0.330549, 0.428269", \
+          "0.25647, 0.292463, 0.348003, 0.389077, 0.491427", \
+          "0.428045, 0.463565, 0.522072, 0.557314, 0.666379", \
+          "0.717641, 0.753579, 0.810918, 0.846254, 0.948856");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1002, 0.1044, 0.0954, 0.0936, 0.1014", \
+          "0.1524, 0.1542, 0.1428, 0.1428, 0.1458", \
+          "0.2298, 0.2322, 0.2274, 0.222, 0.2286", \
+          "0.4788, 0.4788, 0.4776, 0.4734, 0.4698", \
+          "0.8982, 0.8982, 0.8988, 0.897, 0.8898");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.102317, 0.080679, 0.011467, 0.099937, 0.426621", \
+          "0.101225, 0.09026, 0.011397, 0.068034, 0.374193", \
+          "0.099971, 0.093586, 0.030819, 0.035158, 0.311526", \
+          "0.096747, 0.094935, 0.057755, 0.011457, 0.200415", \
+          "0.095844, 0.09724, 0.073778, 0.042294, 0.113322");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.484138, 0.50293, 0.606859, 0.701526, 1.04066", \
+          "0.487225, 0.498023, 0.584734, 0.670559, 0.994423", \
+          "0.489114, 0.49524, 0.564729, 0.639285, 0.936755", \
+          "0.491075, 0.492445, 0.537952, 0.591309, 0.82816", \
+          "0.492158, 0.491412, 0.520662, 0.558249, 0.737838");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.005158, 0.008434, 0.101793, 0.190899, 0.51801", \
+          "0.003299, 0.004385, 0.081909, 0.160558, 0.466463", \
+          "0.002279, 0.002613, 0.063212, 0.128765, 0.404632", \
+          "0.000822, 0.000485, 0.037414, 0.083377, 0.294884", \
+          "0.000188, 0.001586, 0.0218, 0.053148, 0.208492");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.385984, 0.408189, 0.513619, 0.60983, 0.948413", \
+          "0.388736, 0.404395, 0.491864, 0.57777, 0.901913", \
+          "0.390578, 0.401354, 0.470255, 0.545481, 0.842904", \
+          "0.391557, 0.39692, 0.442683, 0.496422, 0.733841", \
+          "0.392442, 0.394668, 0.425226, 0.462881, 0.64279");
+      }
+    }
+    internal_power() {
+      related_pin : "S";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.248758, 0.251756, 0.318847, 0.369557, 0.579522", \
+          "0.238293, 0.248767, 0.313665, 0.364025, 0.566288", \
+          "0.229945, 0.242006, 0.307564, 0.35791, 0.555719", \
+          "0.215231, 0.227409, 0.294036, 0.348422, 0.544486", \
+          "0.206745, 0.219959, 0.286035, 0.340133, 0.533789");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.447295, 0.465965, 0.531806, 0.588429, 0.797614", \
+          "0.435318, 0.445604, 0.525208, 0.58552, 0.787916", \
+          "0.421378, 0.442286, 0.516509, 0.574096, 0.776779", \
+          "0.405478, 0.425694, 0.498665, 0.557992, 0.761159", \
+          "0.395778, 0.414763, 0.486195, 0.544194, 0.745836");
+      }
+    }
+  }
+}
+
+/* ---------------- *
+ * Design : NAND2X1 *
+ * ---------------- */
+cell (NAND2X1) {
+area : 96;
+  cell_leakage_power : 0.0261623;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0177118;
+    rise_capacitance : 0.0177118;
+    fall_capacitance : 0.0177083;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0180112;
+    rise_capacitance : 0.0179539;
+    fall_capacitance : 0.0180112;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.403083;
+    function : "(!(A B))";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.050618, 0.05299, 0.037559, 0.021262, -0.044862", \
+          "0.090341, 0.097952, 0.096887, 0.089145, 0.044217", \
+          "0.149272, 0.15804, 0.16938, 0.171209, 0.152043", \
+          "0.323475, 0.333667, 0.353625, 0.366459, 0.389536", \
+          "0.619139, 0.625153, 0.643098, 0.658467, 0.708391");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.057532, 0.0888, 0.1224, 0.1554, 0.2406", \
+          "0.1026, 0.1212, 0.1632, 0.1986, 0.2976", \
+          "0.1866, 0.195, 0.2328, 0.2646, 0.3756", \
+          "0.4362, 0.4374, 0.4536, 0.477, 0.579", \
+          "0.8568, 0.8568, 0.8592, 0.8688, 0.9354");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.08412, 0.122658, 0.187644, 0.23227, 0.368234", \
+          "0.133223, 0.173084, 0.249212, 0.299945, 0.453624", \
+          "0.210356, 0.24844, 0.332414, 0.390414, 0.561828", \
+          "0.444258, 0.479574, 0.557949, 0.620954, 0.829115", \
+          "0.832864, 0.867038, 0.941334, 1.00001, 1.20776");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0876, 0.105, 0.1416, 0.1686, 0.2448", \
+          "0.156, 0.165, 0.207, 0.2358, 0.3252", \
+          "0.273, 0.2742, 0.3048, 0.3348, 0.4338", \
+          "0.6228, 0.6228, 0.6276, 0.6438, 0.732", \
+          "1.2078, 1.2078, 1.2078, 1.2078, 1.2522");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.050994, 0.0599, 0.061444, 0.055413, 0.021324", \
+          "0.089805, 0.112829, 0.130014, 0.134585, 0.12642", \
+          "0.147571, 0.174805, 0.214511, 0.230068, 0.252106", \
+          "0.322786, 0.349291, 0.404088, 0.444179, 0.525043", \
+          "0.616501, 0.640746, 0.693265, 0.734586, 0.871398");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.055756, 0.0834, 0.1218, 0.1494, 0.2394", \
+          "0.1026, 0.1296, 0.1752, 0.2088, 0.3162", \
+          "0.1848, 0.1968, 0.2496, 0.2862, 0.4074", \
+          "0.4374, 0.4362, 0.4596, 0.4938, 0.6306", \
+          "0.8562, 0.8568, 0.858, 0.8706, 0.9756");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0686, 0.102791, 0.153802, 0.187324, 0.287725", \
+          "0.119023, 0.156792, 0.224118, 0.267771, 0.393386", \
+          "0.196982, 0.234136, 0.313565, 0.366261, 0.517613", \
+          "0.432759, 0.466376, 0.542876, 0.603709, 0.801824", \
+          "0.821787, 0.854876, 0.927876, 0.985407, 1.1885");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0624, 0.093, 0.1284, 0.1476, 0.2256", \
+          "0.135, 0.1494, 0.1932, 0.2196, 0.3126", \
+          "0.2514, 0.255, 0.2874, 0.3198, 0.4218", \
+          "0.6024, 0.6024, 0.6066, 0.6246, 0.7194", \
+          "1.1868, 1.1862, 1.1868, 1.1874, 1.2336");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.05243, 0.035902, 0.041885, 0.108596, 0.347428", \
+          "0.049415, 0.038277, 0.020757, 0.078056, 0.29691", \
+          "0.047477, 0.040295, 0.004607, 0.05133, 0.243265", \
+          "0.045429, 0.041369, 0.014147, 0.017436, 0.160098", \
+          "0.0449, 0.042425, 0.025204, 0.003547, 0.100727");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.222098, 0.250472, 0.331364, 0.40047, 0.645498", \
+          "0.221978, 0.242516, 0.311104, 0.374246, 0.606873", \
+          "0.223326, 0.236671, 0.291136, 0.346414, 0.560211", \
+          "0.225693, 0.230999, 0.26554, 0.30424, 0.474407", \
+          "0.226379, 0.228756, 0.249957, 0.276102, 0.402794");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.048835, 0.032491, 0.039931, 0.10133, 0.320198", \
+          "0.047414, 0.03488, 0.018367, 0.070529, 0.270044", \
+          "0.04617, 0.039208, 0.002135, 0.044184, 0.217706", \
+          "0.044908, 0.04099, 0.01672, 0.011339, 0.138869", \
+          "0.044852, 0.04235, 0.027389, 0.008647, 0.083186");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.156889, 0.181307, 0.258189, 0.32237, 0.545524", \
+          "0.162309, 0.178133, 0.238944, 0.296493, 0.506332", \
+          "0.166899, 0.175783, 0.22275, 0.271915, 0.463457", \
+          "0.173637, 0.175479, 0.203495, 0.237439, 0.389238", \
+          "0.176006, 0.176438, 0.193341, 0.215966, 0.329132");
+      }
+    }
+  }
+}
+
+/* ---------------- *
+ * Design : NAND3X1 *
+ * ---------------- */
+cell (NAND3X1) {
+area : 144;
+  cell_leakage_power : 0.0371207;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0222511;
+    rise_capacitance : 0.0222511;
+    fall_capacitance : 0.0221072;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.022204;
+    rise_capacitance : 0.022204;
+    fall_capacitance : 0.0221403;
+  }
+  pin(C)  {
+    direction : input;
+    capacitance : 0.0226301;
+    rise_capacitance : 0.0225324;
+    fall_capacitance : 0.0226301;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.386439;
+    function : "(!((A B) C))";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.063065, 0.058467, 0.028759, 0.002227, -0.096807", \
+          "0.100322, 0.097286, 0.078741, 0.059139, -0.023316", \
+          "0.156628, 0.154548, 0.1444, 0.131618, 0.069258", \
+          "0.321295, 0.320508, 0.317084, 0.312099, 0.282799", \
+          "0.598619, 0.596485, 0.590934, 0.589206, 0.579448");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0666, 0.09, 0.129, 0.1572, 0.2562", \
+          "0.114, 0.1296, 0.1644, 0.1974, 0.3", \
+          "0.1914, 0.2004, 0.231, 0.2598, 0.3666", \
+          "0.432, 0.4338, 0.4488, 0.4692, 0.5586", \
+          "0.8352, 0.8346, 0.8388, 0.8484, 0.9102");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.124147, 0.164561, 0.246678, 0.304128, 0.479718", \
+          "0.175141, 0.212995, 0.300527, 0.361018, 0.547992", \
+          "0.253843, 0.290844, 0.377535, 0.443662, 0.642004", \
+          "0.488942, 0.523451, 0.603166, 0.667691, 0.890972", \
+          "0.878173, 0.911994, 0.986901, 1.04676, 1.26092");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1314, 0.1434, 0.18, 0.2058, 0.2802", \
+          "0.204, 0.2094, 0.2418, 0.27, 0.354", \
+          "0.3204, 0.3204, 0.3414, 0.3678, 0.4602", \
+          "0.6708, 0.6708, 0.6732, 0.6852, 0.762", \
+          "1.2558, 1.2558, 1.2558, 1.2558, 1.2918");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.060178, 0.060331, 0.045491, 0.029091, -0.038005", \
+          "0.097654, 0.10274, 0.102664, 0.09515, 0.047732", \
+          "0.153403, 0.162733, 0.174782, 0.176451, 0.15465", \
+          "0.318194, 0.329239, 0.352701, 0.366113, 0.390753", \
+          "0.594217, 0.604663, 0.626442, 0.644563, 0.699437");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.066, 0.0924, 0.1356, 0.1704, 0.2646", \
+          "0.1146, 0.1362, 0.1776, 0.213, 0.3192", \
+          "0.1932, 0.204, 0.2424, 0.2754, 0.3906", \
+          "0.432, 0.4332, 0.4524, 0.4794, 0.5874", \
+          "0.8346, 0.8346, 0.8388, 0.8508, 0.9252");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.103758, 0.142637, 0.214764, 0.263466, 0.412065", \
+          "0.154097, 0.192381, 0.27275, 0.326708, 0.491256", \
+          "0.233115, 0.269494, 0.353233, 0.414663, 0.5956", \
+          "0.468607, 0.502046, 0.580242, 0.643115, 0.857417", \
+          "0.857634, 0.890945, 0.964489, 1.02344, 1.23363");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0984, 0.1134, 0.156, 0.1776, 0.2496", \
+          "0.1716, 0.1788, 0.2154, 0.2436, 0.3288", \
+          "0.288, 0.2898, 0.3138, 0.3432, 0.4374", \
+          "0.639, 0.639, 0.6414, 0.6558, 0.738", \
+          "1.224, 1.2234, 1.224, 1.224, 1.2624");
+      }
+    }
+    timing() {
+      related_pin : "C";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.052574, 0.057961, 0.052516, 0.041428, -0.005342", \
+          "0.08508, 0.105469, 0.118295, 0.117778, 0.093831", \
+          "0.141905, 0.16599, 0.197503, 0.209114, 0.213802", \
+          "0.30662, 0.331356, 0.381447, 0.414428, 0.476457", \
+          "0.584826, 0.606219, 0.654025, 0.691932, 0.809832");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0648, 0.093, 0.1284, 0.153, 0.2466", \
+          "0.1134, 0.1362, 0.1812, 0.2118, 0.3198", \
+          "0.1926, 0.2022, 0.2544, 0.2874, 0.4074", \
+          "0.4326, 0.4326, 0.459, 0.4944, 0.6228", \
+          "0.8346, 0.8346, 0.8364, 0.8526, 0.9606");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.076463, 0.113801, 0.170603, 0.20997, 0.324975", \
+          "0.128923, 0.166587, 0.237823, 0.284793, 0.42485", \
+          "0.209418, 0.244544, 0.32564, 0.381097, 0.544265", \
+          "0.446454, 0.479009, 0.554949, 0.616878, 0.822055", \
+          "0.836244, 0.868808, 0.941201, 0.998972, 1.20528");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0684, 0.0876, 0.126, 0.1524, 0.2214", \
+          "0.1416, 0.1476, 0.1914, 0.2196, 0.3042", \
+          "0.2568, 0.258, 0.2886, 0.3186, 0.4158", \
+          "0.6072, 0.6072, 0.6102, 0.6264, 0.7152", \
+          "1.1922, 1.1922, 1.1922, 1.1916, 1.2342");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.055521, 0.040775, 0.03436, 0.106269, 0.37116", \
+          "0.051123, 0.04417, 0.018152, 0.079727, 0.323968", \
+          "0.048514, 0.043811, 0.003845, 0.055658, 0.272607", \
+          "0.046277, 0.042892, 0.013231, 0.02298, 0.188781", \
+          "0.04439, 0.044045, 0.023706, 0.001739, 0.125735");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.39656, 0.419399, 0.502791, 0.578856, 0.85004", \
+          "0.399282, 0.415937, 0.486771, 0.55634, 0.81442", \
+          "0.401056, 0.413041, 0.469933, 0.53044, 0.769979", \
+          "0.403276, 0.408759, 0.444555, 0.487864, 0.681315", \
+          "0.404304, 0.405619, 0.428297, 0.457889, 0.60292");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.053459, 0.038189, 0.038344, 0.105744, 0.350996", \
+          "0.049885, 0.041746, 0.018987, 0.076689, 0.301486", \
+          "0.047474, 0.04189, 0.003546, 0.051007, 0.248281", \
+          "0.045998, 0.041779, 0.014474, 0.017682, 0.164775", \
+          "0.045131, 0.042536, 0.025206, 0.003192, 0.104499");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.300109, 0.317913, 0.400251, 0.469746, 0.721102", \
+          "0.30405, 0.317618, 0.383775, 0.448337, 0.687082", \
+          "0.307957, 0.315832, 0.368245, 0.424418, 0.645347", \
+          "0.313037, 0.31588, 0.348129, 0.387408, 0.565113", \
+          "0.317472, 0.317023, 0.336234, 0.36274, 0.496095");
+      }
+    }
+    internal_power() {
+      related_pin : "C";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.050643, 0.03145, 0.046323, 0.112211, 0.346305", \
+          "0.046794, 0.034369, 0.024412, 0.08096, 0.295526", \
+          "0.045674, 0.038069, 0.007659, 0.05344, 0.241358", \
+          "0.044505, 0.041187, 0.012637, 0.018344, 0.15786", \
+          "0.044531, 0.042453, 0.024445, 0.003487, 0.097954");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.196567, 0.219393, 0.295041, 0.363049, 0.60089", \
+          "0.201831, 0.216187, 0.278552, 0.339013, 0.564111", \
+          "0.210789, 0.216731, 0.264087, 0.31658, 0.52259", \
+          "0.222925, 0.221746, 0.248983, 0.284661, 0.448838", \
+          "0.227537, 0.226371, 0.242053, 0.265531, 0.388051");
+      }
+    }
+  }
+}
+
+/* --------------- *
+ * Design : NOR2X1 *
+ * --------------- */
+cell (NOR2X1) {
+area : 96;
+  cell_leakage_power : 0.0252328;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0222369;
+    rise_capacitance : 0.0219827;
+    fall_capacitance : 0.0222369;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0227534;
+    rise_capacitance : 0.0227534;
+    fall_capacitance : 0.0227451;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.427702;
+    function : "(!(A+B))";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.094223, 0.122979, 0.16574, 0.193293, 0.267001", \
+          "0.138995, 0.168762, 0.224888, 0.258293, 0.348349", \
+          "0.207887, 0.238263, 0.304627, 0.345288, 0.454567", \
+          "0.413428, 0.441918, 0.506489, 0.558761, 0.714238", \
+          "0.754586, 0.782418, 0.843585, 0.892655, 1.06707");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0846, 0.0984, 0.1524, 0.1812, 0.2772", \
+          "0.1416, 0.1518, 0.201, 0.2352, 0.3486", \
+          "0.2352, 0.2382, 0.2766, 0.3168, 0.4368", \
+          "0.516, 0.5172, 0.5256, 0.5502, 0.6726", \
+          "0.987, 0.9876, 0.9876, 0.99, 1.0632");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.07466, 0.08727, 0.096671, 0.101886, 0.105469", \
+          "0.122371, 0.137942, 0.159104, 0.17134, 0.195559", \
+          "0.194672, 0.211483, 0.241865, 0.261172, 0.307247", \
+          "0.416818, 0.430715, 0.462887, 0.490222, 0.567975", \
+          "0.782968, 0.796016, 0.825108, 0.849861, 0.942236");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0834, 0.1074, 0.1512, 0.183, 0.276", \
+          "0.147, 0.162, 0.2016, 0.234, 0.3402", \
+          "0.2544, 0.2598, 0.291, 0.3216, 0.4308", \
+          "0.5838, 0.585, 0.5952, 0.6108, 0.7002", \
+          "1.1352, 1.1352, 1.1352, 1.1412, 1.1874");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.069698, 0.09223, 0.114912, 0.125897, 0.154857", \
+          "0.115517, 0.144789, 0.185713, 0.209021, 0.261995", \
+          "0.18518, 0.214744, 0.274435, 0.307898, 0.389318", \
+          "0.392571, 0.420854, 0.483288, 0.533959, 0.673864", \
+          "0.734171, 0.762343, 0.822754, 0.870133, 1.04028");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.05093, 0.0738, 0.1128, 0.1422, 0.2226", \
+          "0.1104, 0.1206, 0.1704, 0.2022, 0.3072", \
+          "0.2004, 0.2058, 0.2466, 0.2862, 0.402", \
+          "0.4824, 0.4824, 0.4914, 0.5178, 0.6468", \
+          "0.9528, 0.9528, 0.9522, 0.9552, 1.0326");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.06257, 0.085847, 0.117312, 0.133773, 0.178664", \
+          "0.112082, 0.142406, 0.188502, 0.216886, 0.28721", \
+          "0.184816, 0.216357, 0.27928, 0.316598, 0.415703", \
+          "0.404717, 0.434101, 0.499511, 0.551588, 0.702157", \
+          "0.771036, 0.799061, 0.861229, 0.910339, 1.08346");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0816, 0.1098, 0.141, 0.1668, 0.2466", \
+          "0.1434, 0.1632, 0.21, 0.2382, 0.3336", \
+          "0.2544, 0.2598, 0.3012, 0.3378, 0.4428", \
+          "0.5844, 0.585, 0.594, 0.618, 0.7302", \
+          "1.1352, 1.1352, 1.1352, 1.1376, 1.203");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.100086, 0.08835, 0.021973, 0.042922, 0.282125", \
+          "0.099939, 0.094558, 0.038313, 0.017965, 0.239918", \
+          "0.099186, 0.09476, 0.053908, 0.007207, 0.190259", \
+          "0.097375, 0.097924, 0.073708, 0.042464, 0.105685", \
+          "0.096817, 0.099208, 0.084503, 0.064405, 0.04125");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.361304, 0.375347, 0.456966, 0.526368, 0.774725", \
+          "0.363138, 0.373451, 0.439158, 0.500149, 0.731911", \
+          "0.365323, 0.372084, 0.42303, 0.475274, 0.684444", \
+          "0.366137, 0.36892, 0.403071, 0.440931, 0.605128", \
+          "0.366777, 0.36826, 0.390885, 0.417747, 0.542588");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.100184, 0.08453, 0.020046, 0.040552, 0.258492", \
+          "0.08927, 0.082951, 0.035156, 0.015414, 0.215319", \
+          "0.08175, 0.079365, 0.04614, 0.005274, 0.169368", \
+          "0.074412, 0.073407, 0.055576, 0.029446, 0.098956", \
+          "0.071195, 0.069708, 0.059057, 0.042539, 0.04887");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.222695, 0.247471, 0.326606, 0.392012, 0.61604", \
+          "0.224907, 0.242664, 0.30644, 0.363794, 0.573359", \
+          "0.227185, 0.239084, 0.288028, 0.33667, 0.526271", \
+          "0.22894, 0.234764, 0.26538, 0.299626, 0.448438", \
+          "0.229797, 0.232968, 0.252228, 0.275546, 0.386926");
+      }
+    }
+  }
+}
+
+/* --------------- *
+ * Design : NOR3X1 *
+ * --------------- */
+cell (NOR3X1) {
+area : 256;
+  cell_leakage_power : 0.0625284;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0310602;
+    rise_capacitance : 0.0305923;
+    fall_capacitance : 0.0310602;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0311174;
+    rise_capacitance : 0.0306756;
+    fall_capacitance : 0.0311174;
+  }
+  pin(C)  {
+    direction : input;
+    capacitance : 0.0317844;
+    rise_capacitance : 0.0317844;
+    fall_capacitance : 0.0317601;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.419287;
+    function : "(!((A+B)+C))";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.152754, 0.180457, 0.247088, 0.287541, 0.407435", \
+          "0.200512, 0.228206, 0.298269, 0.343387, 0.471277", \
+          "0.27368, 0.300685, 0.369742, 0.421872, 0.563179", \
+          "0.483973, 0.510391, 0.574414, 0.626723, 0.80235", \
+          "0.827612, 0.854153, 0.914819, 0.963549, 1.1408");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1482, 0.1518, 0.1998, 0.2346, 0.3426", \
+          "0.2064, 0.2106, 0.246, 0.2862, 0.4026", \
+          "0.3042, 0.3024, 0.3294, 0.3642, 0.4872", \
+          "0.5868, 0.5874, 0.591, 0.6096, 0.7188", \
+          "1.059, 1.059, 1.059, 1.0602, 1.1202");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.111162, 0.11659, 0.115847, 0.110573, 0.078725", \
+          "0.157555, 0.162776, 0.171299, 0.171274, 0.155812", \
+          "0.229493, 0.239709, 0.24888, 0.253926, 0.257246", \
+          "0.447117, 0.455693, 0.468917, 0.479141, 0.507478", \
+          "0.808867, 0.817906, 0.828051, 0.836125, 0.873727");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.114, 0.1338, 0.1668, 0.201, 0.3204", \
+          "0.1776, 0.1878, 0.2196, 0.2508, 0.3684", \
+          "0.2832, 0.2874, 0.3132, 0.3384, 0.45", \
+          "0.6084, 0.609, 0.618, 0.6318, 0.714", \
+          "1.152, 1.152, 1.1526, 1.1586, 1.2012");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.128504, 0.156723, 0.208181, 0.238346, 0.323305", \
+          "0.174353, 0.202681, 0.262965, 0.299368, 0.399343", \
+          "0.24442, 0.272432, 0.338528, 0.383135, 0.501193", \
+          "0.451667, 0.478959, 0.54169, 0.592924, 0.755369", \
+          "0.793543, 0.820899, 0.880872, 0.929276, 1.10302");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.105, 0.1098, 0.1584, 0.186, 0.2826", \
+          "0.1596, 0.1662, 0.2064, 0.2412, 0.3498", \
+          "0.2556, 0.2544, 0.2862, 0.3216, 0.4398", \
+          "0.5376, 0.5376, 0.543, 0.5634, 0.6774", \
+          "1.008, 1.0086, 1.0086, 1.0098, 1.0746");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.099134, 0.110661, 0.120065, 0.127915, 0.141346", \
+          "0.144803, 0.160931, 0.182812, 0.196907, 0.228736", \
+          "0.219572, 0.236418, 0.2653, 0.285933, 0.34025", \
+          "0.436457, 0.45205, 0.488798, 0.518428, 0.60083", \
+          "0.798336, 0.815172, 0.847517, 0.87517, 0.976568");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1146, 0.1416, 0.183, 0.216, 0.3186", \
+          "0.1782, 0.1926, 0.2376, 0.2652, 0.3774", \
+          "0.2826, 0.2904, 0.3246, 0.3582, 0.4644", \
+          "0.6084, 0.609, 0.6222, 0.6396, 0.7344", \
+          "1.152, 1.152, 1.1526, 1.1598, 1.2084");
+      }
+    }
+    timing() {
+      related_pin : "C";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.08301, 0.109772, 0.137035, 0.154933, 0.193345", \
+          "0.129516, 0.159882, 0.206652, 0.231278, 0.296385", \
+          "0.202683, 0.23089, 0.292905, 0.328253, 0.419645", \
+          "0.412006, 0.440002, 0.501383, 0.551917, 0.69869", \
+          "0.754706, 0.783562, 0.842947, 0.890235, 1.0612");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.059164, 0.0714, 0.1116, 0.1362, 0.2124", \
+          "0.1134, 0.1206, 0.1674, 0.1974, 0.294", \
+          "0.2052, 0.2058, 0.243, 0.2808, 0.3906", \
+          "0.486, 0.4866, 0.4938, 0.5166, 0.6372", \
+          "0.957, 0.957, 0.957, 0.9588, 1.0284");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.06395, 0.086552, 0.113071, 0.126885, 0.164031", \
+          "0.111545, 0.140241, 0.181419, 0.206979, 0.267804", \
+          "0.185132, 0.212086, 0.270257, 0.303683, 0.392876", \
+          "0.401038, 0.428874, 0.48948, 0.5381, 0.674042", \
+          "0.764447, 0.790188, 0.846857, 0.892475, 1.05398");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1032, 0.1386, 0.162, 0.186, 0.2634", \
+          "0.1716, 0.186, 0.2364, 0.2586, 0.3516", \
+          "0.2808, 0.2862, 0.3258, 0.3636, 0.4578", \
+          "0.609, 0.6078, 0.618, 0.6414, 0.753", \
+          "1.152, 1.152, 1.152, 1.1544, 1.2204");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.131065, 0.131611, 0.073869, 0.011834, 0.237163", \
+          "0.129315, 0.132488, 0.086616, 0.031049, 0.201598", \
+          "0.128831, 0.134652, 0.098841, 0.051978, 0.157648", \
+          "0.128206, 0.136966, 0.115238, 0.083835, 0.076353", \
+          "0.127994, 0.138045, 0.125952, 0.105383, 0.010031");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.735777, 0.731908, 0.804381, 0.870758, 1.12931", \
+          "0.735821, 0.732413, 0.792006, 0.850206, 1.09287", \
+          "0.739806, 0.736356, 0.781242, 0.830825, 1.05035", \
+          "0.741399, 0.735498, 0.767423, 0.804083, 0.978528", \
+          "0.741922, 0.736246, 0.759107, 0.785704, 0.920807");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.128196, 0.127751, 0.072673, 0.013607, 0.218591", \
+          "0.123813, 0.124764, 0.082707, 0.030787, 0.185128", \
+          "0.11947, 0.122491, 0.091122, 0.048125, 0.144488", \
+          "0.115159, 0.118347, 0.100359, 0.072257, 0.073501", \
+          "0.113173, 0.115457, 0.104917, 0.086698, 0.017981");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.530429, 0.537729, 0.612321, 0.678689, 0.926266", \
+          "0.534233, 0.542837, 0.59752, 0.655483, 0.8855", \
+          "0.537091, 0.540931, 0.58478, 0.63404, 0.83976", \
+          "0.539785, 0.539628, 0.568191, 0.603192, 0.763737", \
+          "0.540302, 0.538374, 0.557991, 0.582507, 0.703668");
+      }
+    }
+    internal_power() {
+      related_pin : "C";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.153385, 0.13477, 0.072153, 0.013196, 0.21246", \
+          "0.135576, 0.129487, 0.08411, 0.03299, 0.174229", \
+          "0.121696, 0.120814, 0.089029, 0.048567, 0.132365", \
+          "0.105399, 0.105208, 0.089866, 0.064886, 0.068317", \
+          "0.098687, 0.095768, 0.087049, 0.071252, 0.022819");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.321292, 0.345217, 0.432191, 0.50093, 0.736904", \
+          "0.322384, 0.342056, 0.412164, 0.472551, 0.694948", \
+          "0.328056, 0.337857, 0.393229, 0.445136, 0.646544", \
+          "0.331253, 0.335912, 0.369846, 0.406444, 0.565268", \
+          "0.331784, 0.334815, 0.35633, 0.38128, 0.50085");
+      }
+    }
+  }
+}
+
+/* ---------------- *
+ * Design : OAI21X1 *
+ * ---------------- */
+cell (OAI21X1) {
+area : 92;
+  cell_leakage_power : 0.0336415;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0265312;
+    rise_capacitance : 0.0263596;
+    fall_capacitance : 0.0265312;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0270702;
+    rise_capacitance : 0.0270702;
+    fall_capacitance : 0.0270424;
+  }
+  pin(C)  {
+    direction : input;
+    capacitance : 0.0180101;
+    rise_capacitance : 0.017967;
+    fall_capacitance : 0.0180101;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.405483;
+    function : "(!((A+B) C))";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.086997, 0.09469, 0.102524, 0.104145, 0.101809", \
+          "0.124165, 0.134819, 0.149625, 0.157113, 0.166509", \
+          "0.18421, 0.194317, 0.21523, 0.22805, 0.253975", \
+          "0.364258, 0.370313, 0.3933, 0.411967, 0.463753", \
+          "0.65629, 0.661956, 0.681604, 0.699621, 0.765878");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0864, 0.1038, 0.1506, 0.186, 0.2982", \
+          "0.1368, 0.1488, 0.189, 0.2262, 0.3468", \
+          "0.219, 0.225, 0.258, 0.2898, 0.4158", \
+          "0.4704, 0.4704, 0.4842, 0.5046, 0.6066", \
+          "0.8898, 0.8898, 0.8922, 0.9006, 0.9624");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.098103, 0.117874, 0.148161, 0.172002, 0.24093", \
+          "0.144665, 0.164218, 0.201587, 0.228311, 0.310109", \
+          "0.218633, 0.237403, 0.276575, 0.307662, 0.403045", \
+          "0.442142, 0.455856, 0.492552, 0.52567, 0.63554", \
+          "0.8081, 0.821415, 0.852807, 0.8814, 0.992737");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1032, 0.1236, 0.168, 0.1998, 0.3096", \
+          "0.1698, 0.1824, 0.2208, 0.2532, 0.3672", \
+          "0.2802, 0.2826, 0.3102, 0.3408, 0.4506", \
+          "0.6102, 0.6102, 0.618, 0.633, 0.7152", \
+          "1.161, 1.1604, 1.161, 1.1658, 1.206");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.06432, 0.070213, 0.062711, 0.052966, 0.00977", \
+          "0.105016, 0.114343, 0.119459, 0.117466, 0.093522", \
+          "0.166101, 0.175949, 0.190355, 0.196655, 0.196028", \
+          "0.34585, 0.352109, 0.373467, 0.389253, 0.425856", \
+          "0.638719, 0.644948, 0.663437, 0.68045, 0.73914");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.054971, 0.0792, 0.1182, 0.1476, 0.2352", \
+          "0.1074, 0.123, 0.1602, 0.1908, 0.297", \
+          "0.1878, 0.1962, 0.2304, 0.2592, 0.3726", \
+          "0.4392, 0.4392, 0.4548, 0.4746, 0.5754", \
+          "0.8598, 0.8592, 0.8616, 0.8706, 0.933");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.090256, 0.121527, 0.176544, 0.215503, 0.329655", \
+          "0.136801, 0.168698, 0.234643, 0.278287, 0.410338", \
+          "0.209246, 0.242068, 0.315199, 0.364289, 0.513075", \
+          "0.430049, 0.459284, 0.52881, 0.58535, 0.765595", \
+          "0.796105, 0.824558, 0.888846, 0.940851, 1.12828");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1014, 0.1248, 0.162, 0.1866, 0.2694", \
+          "0.171, 0.183, 0.2268, 0.2502, 0.3414", \
+          "0.2802, 0.2826, 0.3168, 0.348, 0.4446", \
+          "0.6102, 0.6102, 0.6174, 0.636, 0.7308", \
+          "1.161, 1.1604, 1.161, 1.1622, 1.2168");
+      }
+    }
+    timing() {
+      related_pin : "C";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.062445, 0.07627, 0.080305, 0.076248, 0.045807", \
+          "0.0965, 0.121046, 0.141542, 0.14639, 0.136477", \
+          "0.149691, 0.176355, 0.216122, 0.231754, 0.249743", \
+          "0.304756, 0.332395, 0.388844, 0.426989, 0.502251", \
+          "0.565487, 0.591129, 0.645979, 0.68865, 0.822292");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.065559, 0.0921, 0.1341, 0.1623, 0.2556", \
+          "0.1107, 0.1308, 0.1812, 0.2142, 0.324", \
+          "0.1803, 0.1929, 0.2454, 0.282, 0.405", \
+          "0.4035, 0.4041, 0.4305, 0.468, 0.6063", \
+          "0.7746, 0.7749, 0.7779, 0.7944, 0.9102");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.075551, 0.111276, 0.168153, 0.20587, 0.31593", \
+          "0.12605, 0.164605, 0.234824, 0.280912, 0.413407", \
+          "0.205143, 0.242753, 0.32323, 0.377329, 0.532734", \
+          "0.441966, 0.475828, 0.553012, 0.613798, 0.813628", \
+          "0.831556, 0.864818, 0.938309, 0.995977, 1.19903");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0939, 0.117, 0.1605, 0.1887, 0.2727", \
+          "0.1644, 0.177, 0.2211, 0.2508, 0.3453", \
+          "0.2805, 0.2844, 0.3162, 0.3474, 0.4503", \
+          "0.6318, 0.6318, 0.6363, 0.6534, 0.7452", \
+          "1.2162, 1.2162, 1.2162, 1.2168, 1.2618");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.103691, 0.088921, 0.00587, 0.094878, 0.423345", \
+          "0.101372, 0.093043, 0.014434, 0.063568, 0.370708", \
+          "0.100096, 0.095484, 0.034286, 0.031396, 0.308173", \
+          "0.096567, 0.09617, 0.059529, 0.014047, 0.197569", \
+          "0.095796, 0.098049, 0.075069, 0.044124, 0.110987");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.465664, 0.487167, 0.590949, 0.687686, 1.02707", \
+          "0.466245, 0.481291, 0.56913, 0.655701, 0.980548", \
+          "0.468949, 0.479052, 0.548495, 0.623572, 0.921915", \
+          "0.470223, 0.474638, 0.52103, 0.574755, 0.81244", \
+          "0.471349, 0.473321, 0.503764, 0.541347, 0.721374");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.102089, 0.081899, 0.004099, 0.081889, 0.364067", \
+          "0.091104, 0.081433, 0.013893, 0.05433, 0.316314", \
+          "0.082948, 0.076999, 0.026844, 0.028762, 0.262565", \
+          "0.075627, 0.071226, 0.041145, 0.003101, 0.174623", \
+          "0.071982, 0.068232, 0.048929, 0.022647, 0.109431");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.327471, 0.359665, 0.456073, 0.538038, 0.827306", \
+          "0.328715, 0.348434, 0.432924, 0.50892, 0.785124", \
+          "0.330943, 0.345576, 0.411051, 0.477251, 0.732958", \
+          "0.33314, 0.340104, 0.38188, 0.429048, 0.634249", \
+          "0.334316, 0.338068, 0.364148, 0.396284, 0.550801");
+      }
+    }
+    internal_power() {
+      related_pin : "C";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.039917, 0.025421, 0.044056, 0.10395, 0.321615", \
+          "0.038445, 0.027707, 0.02289, 0.076886, 0.277398", \
+          "0.03745, 0.030634, 0.008031, 0.050014, 0.229433", \
+          "0.036262, 0.032418, 0.008947, 0.018318, 0.149657", \
+          "0.036161, 0.033715, 0.019044, 0.005086, 0.091927");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.23864, 0.260149, 0.337369, 0.401465, 0.623264", \
+          "0.241704, 0.259122, 0.319202, 0.377187, 0.586033", \
+          "0.246207, 0.256253, 0.304379, 0.353436, 0.544489", \
+          "0.253474, 0.256182, 0.285822, 0.319753, 0.471382", \
+          "0.256034, 0.256796, 0.275212, 0.298605, 0.411567");
+      }
+    }
+  }
+}
+
+/* ---------------- *
+ * Design : OAI22X1 *
+ * ---------------- */
+cell (OAI22X1) {
+area : 160;
+  cell_leakage_power : 0.0438451;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0265291;
+    rise_capacitance : 0.0263982;
+    fall_capacitance : 0.0265291;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0271075;
+    rise_capacitance : 0.0271075;
+    fall_capacitance : 0.0270484;
+  }
+  pin(C)  {
+    direction : input;
+    capacitance : 0.026839;
+    rise_capacitance : 0.026597;
+    fall_capacitance : 0.026839;
+  }
+  pin(D)  {
+    direction : input;
+    capacitance : 0.027353;
+    rise_capacitance : 0.0273351;
+    fall_capacitance : 0.027353;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.399598;
+    function : "(!((A+B) (C+D)))";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.099682, 0.111241, 0.125707, 0.130958, 0.134725", \
+          "0.133657, 0.147421, 0.168529, 0.178751, 0.194475", \
+          "0.186331, 0.200256, 0.228547, 0.244158, 0.276178", \
+          "0.341116, 0.35475, 0.388144, 0.412575, 0.474104", \
+          "0.597603, 0.610573, 0.641493, 0.667358, 0.75262");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0975, 0.1137, 0.1611, 0.1959, 0.3141", \
+          "0.1443, 0.1539, 0.2004, 0.2361, 0.36", \
+          "0.216, 0.2229, 0.2595, 0.2937, 0.4203", \
+          "0.4365, 0.4368, 0.4539, 0.4788, 0.5916", \
+          "0.8049, 0.8052, 0.8082, 0.8196, 0.8982");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.113978, 0.132569, 0.165967, 0.191954, 0.267294", \
+          "0.159849, 0.178686, 0.216762, 0.246098, 0.333534", \
+          "0.23333, 0.250801, 0.291932, 0.323369, 0.423869", \
+          "0.456313, 0.469707, 0.506912, 0.540647, 0.65344", \
+          "0.822465, 0.835691, 0.867524, 0.896344, 1.00984");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1371, 0.1512, 0.1992, 0.2316, 0.3435", \
+          "0.2043, 0.2148, 0.2514, 0.285, 0.3963", \
+          "0.3135, 0.3171, 0.342, 0.3723, 0.4794", \
+          "0.6429, 0.6429, 0.6513, 0.6651, 0.7473", \
+          "1.1937, 1.194, 1.1937, 1.1988, 1.2387");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.081539, 0.090955, 0.09112, 0.085746, 0.052372", \
+          "0.11656, 0.129773, 0.141137, 0.143475, 0.128017", \
+          "0.170085, 0.183583, 0.206376, 0.21591, 0.222654", \
+          "0.324717, 0.33956, 0.370637, 0.392359, 0.439292", \
+          "0.582037, 0.596115, 0.625681, 0.650638, 0.72841");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0716, 0.0927, 0.1332, 0.1608, 0.2559", \
+          "0.117, 0.1326, 0.1737, 0.2064, 0.3123", \
+          "0.1893, 0.1971, 0.2352, 0.2667, 0.3822", \
+          "0.4101, 0.4098, 0.4284, 0.4539, 0.5637", \
+          "0.7782, 0.7782, 0.7815, 0.7935, 0.873");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.103071, 0.136511, 0.196033, 0.2371, 0.359202", \
+          "0.149747, 0.184296, 0.251687, 0.297222, 0.434936", \
+          "0.223843, 0.255702, 0.330577, 0.381283, 0.534627", \
+          "0.444205, 0.473707, 0.543223, 0.600074, 0.78456", \
+          "0.810436, 0.838855, 0.903388, 0.955822, 1.14488");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.135, 0.153, 0.198, 0.2253, 0.3162", \
+          "0.2025, 0.2139, 0.2574, 0.2862, 0.381", \
+          "0.3132, 0.3162, 0.3477, 0.378, 0.4806", \
+          "0.6429, 0.6429, 0.6501, 0.6681, 0.7626", \
+          "1.1937, 1.194, 1.1937, 1.1955, 1.2489");
+      }
+    }
+    timing() {
+      related_pin : "C";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.094011, 0.119345, 0.148763, 0.164912, 0.201711", \
+          "0.128233, 0.155704, 0.19659, 0.218118, 0.268019", \
+          "0.181365, 0.209177, 0.26149, 0.290178, 0.358289", \
+          "0.337698, 0.364705, 0.423741, 0.467916, 0.576285", \
+          "0.598193, 0.623495, 0.679723, 0.724465, 0.873648");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0906, 0.1125, 0.1605, 0.1959, 0.3093", \
+          "0.1383, 0.1539, 0.2022, 0.2397, 0.3633", \
+          "0.2118, 0.2196, 0.2652, 0.3048, 0.4323", \
+          "0.4347, 0.4344, 0.4554, 0.4878, 0.621", \
+          "0.8061, 0.8064, 0.8082, 0.822, 0.9252");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.089831, 0.10456, 0.130648, 0.146494, 0.187159", \
+          "0.138797, 0.156647, 0.187489, 0.209048, 0.265441", \
+          "0.213914, 0.231533, 0.267672, 0.292988, 0.368118", \
+          "0.439506, 0.452477, 0.487515, 0.518101, 0.614453", \
+          "0.80675, 0.819261, 0.850083, 0.877145, 0.98101");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1125, 0.1308, 0.1749, 0.2145, 0.3174", \
+          "0.1797, 0.1911, 0.2307, 0.2616, 0.372", \
+          "0.288, 0.294, 0.3207, 0.3498, 0.4554", \
+          "0.6189, 0.6189, 0.6282, 0.6426, 0.7239", \
+          "1.1697, 1.1697, 1.1697, 1.1748, 1.2153");
+      }
+    }
+    timing() {
+      related_pin : "D";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.076469, 0.096747, 0.109204, 0.113571, 0.107963", \
+          "0.111465, 0.13694, 0.165492, 0.177281, 0.19249", \
+          "0.16471, 0.191876, 0.23748, 0.258745, 0.298946", \
+          "0.321282, 0.349002, 0.406075, 0.447386, 0.539527", \
+          "0.583104, 0.608856, 0.663873, 0.707588, 0.849903");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.07007, 0.0894, 0.1296, 0.159, 0.2499", \
+          "0.114, 0.1296, 0.1755, 0.2103, 0.3156", \
+          "0.1869, 0.1923, 0.2391, 0.2784, 0.3966", \
+          "0.408, 0.408, 0.4296, 0.4629, 0.5955", \
+          "0.7794, 0.7791, 0.7809, 0.795, 0.9009");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.081842, 0.111738, 0.157035, 0.187577, 0.274964", \
+          "0.130091, 0.16249, 0.22058, 0.258801, 0.366375", \
+          "0.20441, 0.236522, 0.305139, 0.350263, 0.480249", \
+          "0.427694, 0.456012, 0.523688, 0.57838, 0.747793", \
+          "0.794838, 0.822428, 0.886097, 0.93691, 1.11868");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1128, 0.1395, 0.1785, 0.2028, 0.2886", \
+          "0.18, 0.1911, 0.2379, 0.2661, 0.3585", \
+          "0.2883, 0.2925, 0.3264, 0.3606, 0.4584", \
+          "0.6189, 0.6189, 0.6267, 0.6462, 0.7434", \
+          "1.1697, 1.17, 1.1697, 1.1712, 1.2267");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.093005, 0.080247, 0.001934, 0.085385, 0.403434", \
+          "0.090562, 0.084903, 0.014984, 0.05941, 0.35681", \
+          "0.088775, 0.088039, 0.031366, 0.030932, 0.30022", \
+          "0.087613, 0.088242, 0.053589, 0.009783, 0.195844", \
+          "0.086783, 0.089764, 0.067932, 0.037722, 0.114082");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.567336, 0.585153, 0.689057, 0.784135, 1.1216", \
+          "0.568835, 0.582947, 0.667664, 0.754389, 1.0767", \
+          "0.571537, 0.580912, 0.648621, 0.723309, 1.02002", \
+          "0.573152, 0.57716, 0.623394, 0.676483, 0.913095", \
+          "0.574389, 0.57618, 0.606405, 0.643855, 0.823087");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.088523, 0.074773, 0.001582, 0.074304, 0.346332", \
+          "0.079427, 0.073381, 0.012304, 0.051299, 0.305006", \
+          "0.073028, 0.069307, 0.022515, 0.030143, 0.257198", \
+          "0.066606, 0.062886, 0.034537, 0.006264, 0.175", \
+          "0.063588, 0.059838, 0.041326, 0.01578, 0.113547");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.430123, 0.457734, 0.554171, 0.634838, 0.922629", \
+          "0.43099, 0.452905, 0.53215, 0.60695, 0.882694", \
+          "0.433449, 0.44714, 0.511884, 0.57748, 0.831513", \
+          "0.436012, 0.442804, 0.48414, 0.530831, 0.735056", \
+          "0.437427, 0.441044, 0.466852, 0.498768, 0.652605");
+      }
+    }
+    internal_power() {
+      related_pin : "C";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.090603, 0.078026, 0.007481, 0.084047, 0.3759", \
+          "0.089934, 0.082741, 0.014161, 0.057582, 0.331297", \
+          "0.089344, 0.082945, 0.033617, 0.027111, 0.276966", \
+          "0.088647, 0.087095, 0.056246, 0.017279, 0.17628", \
+          "0.088603, 0.08911, 0.069647, 0.044049, 0.094834");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.481415, 0.498377, 0.596217, 0.678989, 0.974821", \
+          "0.487389, 0.499611, 0.577622, 0.652428, 0.93267", \
+          "0.494185, 0.499163, 0.562962, 0.62723, 0.88359", \
+          "0.501207, 0.502203, 0.544424, 0.590529, 0.795418", \
+          "0.50424, 0.504592, 0.532487, 0.565813, 0.723169");
+      }
+    }
+    internal_power() {
+      related_pin : "D";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.084454, 0.07014, 0.007653, 0.07698, 0.333208", \
+          "0.077041, 0.068312, 0.011785, 0.052152, 0.291466", \
+          "0.07137, 0.065161, 0.024364, 0.026952, 0.243531", \
+          "0.065249, 0.061466, 0.036845, 0.006308, 0.160212", \
+          "0.061641, 0.059102, 0.043482, 0.021601, 0.097281");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.346181, 0.372918, 0.463552, 0.537378, 0.797827", \
+          "0.351602, 0.370931, 0.443684, 0.511436, 0.758151", \
+          "0.35689, 0.369502, 0.426648, 0.485184, 0.711665", \
+          "0.364201, 0.368483, 0.405549, 0.447039, 0.628421", \
+          "0.366958, 0.369421, 0.393291, 0.42183, 0.559392");
+      }
+    }
+  }
+}
+
+/* -------------- *
+ * Design : OR2X1 *
+ * -------------- */
+cell (OR2X1) {
+area : 128;
+  cell_leakage_power : 0.0494157;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0228029;
+    rise_capacitance : 0.0228029;
+    fall_capacitance : 0.0226707;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0222093;
+    rise_capacitance : 0.0221164;
+    fall_capacitance : 0.0222093;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.409507;
+    function : "(A+B)";
+    timing() {
+      related_pin : "A";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.116621, 0.141019, 0.171773, 0.182263, 0.225689", \
+          "0.169773, 0.19202, 0.219578, 0.239302, 0.281571", \
+          "0.246589, 0.272622, 0.29888, 0.317902, 0.362902", \
+          "0.479521, 0.506335, 0.535004, 0.551825, 0.599689", \
+          "0.869562, 0.895033, 0.923384, 0.940276, 0.989101");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0696, 0.072, 0.0816, 0.0798, 0.0978", \
+          "0.1332, 0.1356, 0.1404, 0.1434, 0.1542", \
+          "0.2478, 0.249, 0.2508, 0.2538, 0.2634", \
+          "0.5982, 0.5982, 0.5994, 0.5994, 0.6054", \
+          "1.1826, 1.1832, 1.1826, 1.1832, 1.1856");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.122276, 0.135675, 0.17137, 0.193173, 0.240241", \
+          "0.171985, 0.193193, 0.225337, 0.245767, 0.298625", \
+          "0.242362, 0.266203, 0.300365, 0.320535, 0.377601", \
+          "0.448049, 0.470992, 0.511035, 0.534031, 0.594333", \
+          "0.788885, 0.811967, 0.851586, 0.875565, 0.940598");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0648, 0.0606, 0.0738, 0.0768, 0.0876", \
+          "0.1176, 0.1176, 0.126, 0.1284, 0.1398", \
+          "0.2034, 0.207, 0.2136, 0.2148, 0.2262", \
+          "0.4812, 0.4812, 0.486, 0.4884, 0.4992", \
+          "0.9504, 0.9504, 0.9516, 0.9534, 0.9624");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.151161, 0.179944, 0.232366, 0.259262, 0.344699", \
+          "0.202914, 0.231278, 0.282411, 0.312468, 0.40075", \
+          "0.282686, 0.310784, 0.359634, 0.391715, 0.47917", \
+          "0.516346, 0.543933, 0.593682, 0.624603, 0.712439", \
+          "0.904994, 0.932467, 0.982343, 1.01297, 1.10095");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0726, 0.069, 0.0816, 0.09, 0.0924", \
+          "0.1362, 0.1386, 0.1404, 0.1464, 0.1524", \
+          "0.2484, 0.2502, 0.2514, 0.2532, 0.258", \
+          "0.5988, 0.5988, 0.5994, 0.6006, 0.603", \
+          "1.1832, 1.1832, 1.1838, 1.1838, 1.185");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.133001, 0.1391, 0.156334, 0.165958, 0.175645", \
+          "0.181856, 0.19189, 0.212139, 0.223187, 0.239212", \
+          "0.2521, 0.264486, 0.288531, 0.300354, 0.322806", \
+          "0.458413, 0.474315, 0.496047, 0.509051, 0.5424", \
+          "0.799487, 0.815231, 0.836946, 0.850252, 0.886582");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0624, 0.0648, 0.0804, 0.0804, 0.0972", \
+          "0.1182, 0.117, 0.1284, 0.1344, 0.1476", \
+          "0.2022, 0.207, 0.2112, 0.2172, 0.2346", \
+          "0.4806, 0.4812, 0.4836, 0.4866, 0.5022", \
+          "0.9504, 0.9504, 0.951, 0.9522, 0.9594");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.017102, 0.034419, 0.107899, 0.180566, 0.418477", \
+          "0.018728, 0.0344, 0.111465, 0.175924, 0.407725", \
+          "0.019969, 0.036876, 0.109863, 0.173038, 0.401608", \
+          "0.020865, 0.037922, 0.108322, 0.172868, 0.397457", \
+          "0.017177, 0.03847, 0.108832, 0.172878, 0.396039");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.312329, 0.342105, 0.425274, 0.49504, 0.742271", \
+          "0.316757, 0.351907, 0.431906, 0.490856, 0.732378", \
+          "0.319372, 0.353006, 0.431303, 0.496617, 0.726868", \
+          "0.320371, 0.353453, 0.433133, 0.498296, 0.72632", \
+          "0.320848, 0.353964, 0.433844, 0.498794, 0.725373");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.015274, 0.033162, 0.119309, 0.185392, 0.451048", \
+          "0.016943, 0.031026, 0.11302, 0.180911, 0.440258", \
+          "0.0214, 0.03617, 0.109436, 0.177422, 0.431534", \
+          "0.022433, 0.036916, 0.108068, 0.174535, 0.422962", \
+          "0.022904, 0.037408, 0.108385, 0.174146, 0.419879");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.447211, 0.469718, 0.557633, 0.637211, 0.910248", \
+          "0.456807, 0.472787, 0.560509, 0.633945, 0.898334", \
+          "0.459226, 0.475175, 0.561583, 0.635482, 0.892014", \
+          "0.458211, 0.478494, 0.561743, 0.63445, 0.887859", \
+          "0.458879, 0.478706, 0.562633, 0.634448, 0.886057");
+      }
+    }
+  }
+}
+
+/* -------------- *
+ * Design : OR2X2 *
+ * -------------- */
+cell (OR2X2) {
+area : 128;
+  cell_leakage_power : 0.0500804;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0228068;
+    rise_capacitance : 0.0228068;
+    fall_capacitance : 0.02268;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0221869;
+    rise_capacitance : 0.0220189;
+    fall_capacitance : 0.0221869;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.830878;
+    function : "(A+B)";
+    timing() {
+      related_pin : "A";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.149923, 0.175661, 0.214279, 0.240462, 0.293201", \
+          "0.199427, 0.226702, 0.268998, 0.29309, 0.351953", \
+          "0.278739, 0.306228, 0.346649, 0.370717, 0.432611", \
+          "0.508794, 0.535027, 0.576267, 0.599595, 0.661745", \
+          "0.89133, 0.917648, 0.958436, 0.981751, 1.04404");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0756, 0.0792, 0.087, 0.0894, 0.1032", \
+          "0.1398, 0.1398, 0.1452, 0.1494, 0.1632", \
+          "0.2478, 0.249, 0.252, 0.2532, 0.2646", \
+          "0.5928, 0.5922, 0.5934, 0.594, 0.5988", \
+          "1.17, 1.17, 1.17, 1.17, 1.1724");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.151974, 0.179103, 0.219752, 0.247739, 0.316746", \
+          "0.206425, 0.233336, 0.277136, 0.304366, 0.374008", \
+          "0.28184, 0.311448, 0.354366, 0.381045, 0.453258", \
+          "0.490585, 0.520089, 0.565364, 0.594056, 0.668795", \
+          "0.835088, 0.864651, 0.909519, 0.938399, 1.0172");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0738, 0.0732, 0.0798, 0.0864, 0.0948", \
+          "0.1242, 0.126, 0.132, 0.138, 0.1464", \
+          "0.2124, 0.2142, 0.2214, 0.2214, 0.2346", \
+          "0.4902, 0.4902, 0.4932, 0.4968, 0.5046", \
+          "0.9642, 0.9642, 0.9648, 0.966, 0.9732");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.183048, 0.213362, 0.265391, 0.301296, 0.396946", \
+          "0.232641, 0.263758, 0.320597, 0.356245, 0.454443", \
+          "0.310805, 0.341355, 0.398071, 0.433587, 0.532794", \
+          "0.541276, 0.570536, 0.62756, 0.661687, 0.759848", \
+          "0.924026, 0.953291, 1.00993, 1.0439, 1.1419");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0804, 0.0804, 0.09, 0.09, 0.1008", \
+          "0.1422, 0.1416, 0.147, 0.15, 0.1608", \
+          "0.2508, 0.2508, 0.2532, 0.2556, 0.2622", \
+          "0.5934, 0.5934, 0.594, 0.5946, 0.597", \
+          "1.1706, 1.1706, 1.1706, 1.1712, 1.1718");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.161913, 0.175793, 0.198165, 0.211427, 0.233117", \
+          "0.216632, 0.23218, 0.254804, 0.269345, 0.298561", \
+          "0.290588, 0.306249, 0.331226, 0.348782, 0.382841", \
+          "0.499534, 0.515937, 0.542813, 0.559284, 0.601717", \
+          "0.844164, 0.860559, 0.886871, 0.903663, 0.948099");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0738, 0.0714, 0.0846, 0.0852, 0.1044", \
+          "0.126, 0.126, 0.1332, 0.1392, 0.1572", \
+          "0.213, 0.2136, 0.2178, 0.2238, 0.2406", \
+          "0.4902, 0.4902, 0.4926, 0.495, 0.5082", \
+          "0.9642, 0.9642, 0.9648, 0.9654, 0.9714");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.143606, 0.159455, 0.247567, 0.323718, 0.583509", \
+          "0.146236, 0.159361, 0.239709, 0.307446, 0.554686", \
+          "0.151142, 0.166343, 0.233162, 0.300645, 0.535376", \
+          "0.152838, 0.166825, 0.23068, 0.293997, 0.516851", \
+          "0.15358, 0.166966, 0.230603, 0.291821, 0.509766");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.412259, 0.447604, 0.512618, 0.603395, 0.875806", \
+          "0.422527, 0.451273, 0.530228, 0.60005, 0.844451", \
+          "0.425524, 0.455382, 0.526235, 0.593507, 0.826909", \
+          "0.428475, 0.455919, 0.527383, 0.589655, 0.817678", \
+          "0.429846, 0.456069, 0.528202, 0.589279, 0.812706");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.158475, 0.176916, 0.258663, 0.340389, 0.62443", \
+          "0.15613, 0.171011, 0.246698, 0.323213, 0.593404", \
+          "0.154116, 0.166576, 0.235641, 0.307781, 0.570045", \
+          "0.151151, 0.164221, 0.230976, 0.298574, 0.546499", \
+          "0.151983, 0.163875, 0.230365, 0.295306, 0.537133");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.03, 0.08, 0.16, 0.4, 0.8");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.542212, 0.568381, 0.653969, 0.748461, 1.04478", \
+          "0.563282, 0.58098, 0.658463, 0.73345, 1.01195", \
+          "0.565298, 0.582109, 0.658657, 0.730383, 0.994315", \
+          "0.564066, 0.581296, 0.658311, 0.726096, 0.98029", \
+          "0.566105, 0.581601, 0.657839, 0.725935, 0.974505");
+      }
+    }
+  }
+}
+
+/* --------------- *
+ * Design : PADINC *
+ * --------------- */
+cell (PADINC) {
+ pad_cell : true;
+area : 12000;
+  cell_leakage_power : 0.748445;
+  pin(DI)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 4.89099;
+    function : "YPAD";
+    timing() {
+      related_pin : "YPAD";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.152534, 0.179221, 0.20102, 0.224548, 0.227205, 0.223333", \
+          "0.22519, 0.257474, 0.280681, 0.299959, 0.305456, 0.30549", \
+          "0.340609, 0.372848, 0.396474, 0.417671, 0.424803, 0.427446", \
+          "0.632929, 0.668257, 0.691791, 0.710987, 0.718586, 0.725458", \
+          "0.795209, 0.830454, 0.853988, 0.873846, 0.881408, 0.888529", \
+          "0.957336, 0.992869, 1.01641, 1.03607, 1.04444, 1.05094");
+      }
+      rise_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.0618, 0.058956, 0.069, 0.0768, 0.0822, 0.0876", \
+          "0.15, 0.1536, 0.159, 0.1656, 0.1692, 0.1764", \
+          "0.3162, 0.3174, 0.3204, 0.324, 0.3288, 0.3378", \
+          "0.7536, 0.7536, 0.7542, 0.7566, 0.759, 0.7656", \
+          "0.9972, 0.9966, 0.9972, 0.999, 1.0008, 1.0068", \
+          "1.2402, 1.2402, 1.2402, 1.2414, 1.2432, 1.2474");
+      }
+      cell_fall(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.172083, 0.219014, 0.273229, 0.36053, 0.413009, 0.504921", \
+          "0.231464, 0.283317, 0.342902, 0.428156, 0.483676, 0.581767", \
+          "0.318245, 0.371457, 0.43331, 0.522719, 0.579321, 0.681297", \
+          "0.530621, 0.585515, 0.648208, 0.738671, 0.796486, 0.903578", \
+          "0.647548, 0.702226, 0.765212, 0.856545, 0.914527, 1.02242", \
+          "0.764219, 0.819181, 0.882102, 0.973635, 1.03241, 1.14013");
+      }
+      fall_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.045496, 0.049778, 0.057394, 0.0636, 0.0666, 0.0756", \
+          "0.1128, 0.114, 0.1158, 0.1272, 0.1302, 0.1386", \
+          "0.2178, 0.219, 0.222, 0.2286, 0.2328, 0.2442", \
+          "0.5058, 0.507, 0.5088, 0.513, 0.516, 0.5238", \
+          "0.6696, 0.6702, 0.6702, 0.6744, 0.6762, 0.6828", \
+          "0.8328, 0.8328, 0.8334, 0.8364, 0.8382, 0.8442");
+      }
+    }
+    internal_power() {
+      related_pin : "YPAD";
+      rise_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "2.41284, 2.65791, 3.496, 5.03716, 6.18986, 8.56649", \
+          "2.42437, 2.66532, 3.38914, 4.88648, 6.00418, 8.29957", \
+          "2.4729, 2.68743, 3.36438, 4.7715, 5.88312, 8.17552", \
+          "2.5142, 2.71198, 3.36768, 4.77117, 5.84479, 8.04955", \
+          "2.52137, 2.72219, 3.37948, 4.77758, 5.84596, 8.03764", \
+          "2.52545, 2.72845, 3.38799, 4.78235, 5.84811, 8.03265");
+      }
+      fall_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "6.55365, 6.90576, 7.72103, 9.41953, 10.6374, 13.1264", \
+          "6.62455, 6.98144, 7.72955, 9.25512, 10.4623, 12.8509", \
+          "6.70304, 7.03546, 7.78007, 9.33944, 10.4694, 12.75", \
+          "6.74823, 7.07908, 7.83794, 9.36067, 10.4783, 12.7397", \
+          "6.75673, 7.088, 7.84989, 9.36809, 10.483, 12.7368", \
+          "6.76159, 7.09342, 7.85793, 9.37331, 10.4879, 12.7372");
+      }
+    }
+  }
+  pin(YPAD)  {
+ is_pad : true;
+    direction : input;
+    capacitance : 0.6369;
+    rise_capacitance : 0.6369;
+    fall_capacitance : 0.635915;
+  }
+}
+
+/* ----------------- *
+ * Design : PADINOUT *
+ * ----------------- */
+cell (PADINOUT) {
+ pad_cell : true;
+area : 12000;
+  cell_leakage_power : 0.766836;
+  pin(DI)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 4.89099;
+    function : "YPAD";
+    timing() {
+      related_pin : "YPAD";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.152534, 0.179221, 0.20102, 0.224548, 0.227205, 0.223333", \
+          "0.22519, 0.257474, 0.280681, 0.299959, 0.305456, 0.30549", \
+          "0.340609, 0.372848, 0.396474, 0.417671, 0.424803, 0.427446", \
+          "0.632929, 0.668257, 0.691791, 0.710987, 0.718586, 0.725458", \
+          "0.795209, 0.830454, 0.853988, 0.873846, 0.881408, 0.888529", \
+          "0.957336, 0.992869, 1.01641, 1.03607, 1.04444, 1.05094");
+      }
+      rise_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.0618, 0.058956, 0.069, 0.0768, 0.0822, 0.0876", \
+          "0.15, 0.1536, 0.159, 0.1656, 0.1692, 0.1764", \
+          "0.3162, 0.3174, 0.3204, 0.324, 0.3288, 0.3378", \
+          "0.7536, 0.7536, 0.7542, 0.7566, 0.759, 0.7656", \
+          "0.9972, 0.9966, 0.9972, 0.999, 1.0008, 1.0068", \
+          "1.2402, 1.2402, 1.2402, 1.2414, 1.2432, 1.2474");
+      }
+      cell_fall(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.172083, 0.219014, 0.273229, 0.36053, 0.413009, 0.504921", \
+          "0.231464, 0.283317, 0.342902, 0.428156, 0.483676, 0.581767", \
+          "0.318245, 0.371457, 0.43331, 0.522719, 0.579321, 0.681297", \
+          "0.530621, 0.585515, 0.648208, 0.738671, 0.796486, 0.903578", \
+          "0.647548, 0.702226, 0.765212, 0.856545, 0.914527, 1.02242", \
+          "0.764219, 0.819181, 0.882102, 0.973635, 1.03241, 1.14013");
+      }
+      fall_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.045496, 0.049778, 0.057394, 0.0636, 0.0666, 0.0756", \
+          "0.1128, 0.114, 0.1158, 0.1272, 0.1302, 0.1386", \
+          "0.2178, 0.219, 0.222, 0.2286, 0.2328, 0.2442", \
+          "0.5058, 0.507, 0.5088, 0.513, 0.516, 0.5238", \
+          "0.6696, 0.6702, 0.6702, 0.6744, 0.6762, 0.6828", \
+          "0.8328, 0.8328, 0.8334, 0.8364, 0.8382, 0.8442");
+      }
+    }
+    internal_power() {
+      related_pin : "YPAD";
+      rise_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "2.41281, 2.65792, 3.49596, 5.03709, 6.18999, 8.56634", \
+          "2.42436, 2.66526, 3.38904, 4.88673, 6.00412, 8.29948", \
+          "2.47281, 2.68712, 3.36427, 4.77196, 5.88273, 8.17544", \
+          "2.51386, 2.71212, 3.3668, 4.77168, 5.84351, 8.0495", \
+          "2.52086, 2.7224, 3.37831, 4.77813, 5.84384, 8.03767", \
+          "2.52476, 2.72887, 3.3865, 4.78295, 5.84519, 8.03276");
+      }
+      fall_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "6.55368, 6.90568, 7.7209, 9.41948, 10.6373, 13.1263", \
+          "6.62462, 6.98136, 7.72936, 9.25511, 10.4624, 12.8511", \
+          "6.70324, 7.03534, 7.77985, 9.33938, 10.4696, 12.7501", \
+          "6.74878, 7.07907, 7.83803, 9.36065, 10.4785, 12.7398", \
+          "6.75747, 7.08801, 7.8501, 9.36817, 10.4833, 12.7368", \
+          "6.76252, 7.09344, 7.85825, 9.37348, 10.4884, 12.737");
+      }
+    }
+  }
+  pin(DO)  {
+    direction : input;
+    capacitance : 0.252195;
+    rise_capacitance : 0.251023;
+    fall_capacitance : 0.252195;
+  }
+  pin(OEN)  {
+    direction : input;
+    capacitance : 0.0502646;
+    rise_capacitance : 0.0502646;
+    fall_capacitance : 0.0500636;
+    internal_power() {
+      rise_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0, 0, 0, 0, 0, 0");
+      }
+      fall_power(passive_energy_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("10.2397, 10.2835, 10.3871, 10.7385, 11.0196, 11.557");
+      }
+    }
+  }
+  pin(YPAD)  {
+ is_pad : true;
+    direction : inout;
+ drive_current : 2.0;
+    capacitance : 0.636904;
+    rise_capacitance : 0.636904;
+    fall_capacitance : 0.635915;
+    max_capacitance : 9.39352;
+    function : "DO";
+    three_state : "(!OEN)";
+    timing() {
+      related_pin : "DO";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.592524, 0.620346, 0.641214, 0.663974, 0.66899, 0.671428", \
+          "0.646934, 0.674846, 0.695651, 0.719378, 0.724583, 0.727539", \
+          "0.725028, 0.753077, 0.774274, 0.797934, 0.803839, 0.807345", \
+          "0.888457, 0.916215, 0.937559, 0.96169, 0.967811, 0.971692", \
+          "0.969026, 0.996925, 1.01844, 1.04264, 1.04857, 1.05254", \
+          "1.04685, 1.07464, 1.09595, 1.12035, 1.12647, 1.13049");
+      }
+      rise_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.1746, 0.1764, 0.1722, 0.1752, 0.1788, 0.18", \
+          "0.2214, 0.2208, 0.222, 0.222, 0.2226, 0.2238", \
+          "0.297, 0.2976, 0.2988, 0.2982, 0.2982, 0.2994", \
+          "0.4812, 0.4812, 0.4818, 0.4818, 0.4818, 0.4818", \
+          "0.582, 0.582, 0.582, 0.582, 0.5826, 0.582", \
+          "0.6828, 0.6828, 0.6828, 0.6828, 0.6834, 0.6828");
+      }
+      cell_fall(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.58905, 0.639829, 0.693677, 0.779719, 0.834977, 0.933865", \
+          "0.626019, 0.676719, 0.731176, 0.817648, 0.872564, 0.971599", \
+          "0.683105, 0.733828, 0.788131, 0.87412, 0.929892, 1.02901", \
+          "0.805329, 0.856124, 0.910615, 0.996383, 1.05249, 1.15196", \
+          "0.866553, 0.917379, 0.971785, 1.05805, 1.11418, 1.21361", \
+          "0.925606, 0.976455, 1.03104, 1.11719, 1.17367, 1.27317");
+      }
+      fall_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.2256, 0.2244, 0.2226, 0.2202, 0.2154, 0.2088", \
+          "0.2508, 0.249, 0.2472, 0.2454, 0.2394, 0.2352", \
+          "0.2952, 0.2946, 0.2934, 0.2898, 0.285, 0.282", \
+          "0.4092, 0.4086, 0.408, 0.4056, 0.4032, 0.3996", \
+          "0.477, 0.4758, 0.4764, 0.474, 0.4716, 0.4698", \
+          "0.5496, 0.549, 0.5484, 0.5466, 0.546, 0.5424");
+      }
+    }
+    timing() {
+      related_pin : "OEN";
+      timing_sense : positive_unate;
+      timing_type : three_state_enable;
+      cell_rise(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "1.07279, 1.12151, 1.18206, 1.26323, 1.3023, 1.36449", \
+          "1.13026, 1.179, 1.23933, 1.32131, 1.36041, 1.42189", \
+          "1.21044, 1.25924, 1.31955, 1.40114, 1.44009, 1.50202", \
+          "1.37436, 1.4231, 1.48297, 1.5651, 1.60386, 1.666", \
+          "1.45489, 1.50365, 1.56369, 1.6459, 1.68486, 1.74679", \
+          "1.53262, 1.58137, 1.6412, 1.72342, 1.76224, 1.82435");
+      }
+      rise_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.1848, 0.1842, 0.186, 0.1848, 0.1872, 0.1842", \
+          "0.2304, 0.2304, 0.231, 0.2304, 0.2304, 0.231", \
+          "0.3048, 0.3048, 0.3048, 0.3042, 0.3036, 0.3048", \
+          "0.4854, 0.4854, 0.486, 0.486, 0.486, 0.486", \
+          "0.5856, 0.5856, 0.5856, 0.5862, 0.5856, 0.5856", \
+          "0.6858, 0.6852, 0.6858, 0.6852, 0.6852, 0.6858");
+      }
+      cell_fall(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.65438, 0.704742, 0.772802, 0.852188, 0.893105, 0.956762", \
+          "0.70321, 0.753575, 0.821632, 0.900806, 0.941926, 1.00558", \
+          "0.771359, 0.821715, 0.889757, 0.968599, 1.00989, 1.0735", \
+          "0.910023, 0.960378, 1.02848, 1.10741, 1.14866, 1.21243", \
+          "0.978483, 1.02884, 1.09687, 1.17587, 1.21716, 1.28072", \
+          "1.04406, 1.09441, 1.16238, 1.24124, 1.28272, 1.34633");
+      }
+      fall_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.1392, 0.1398, 0.1392, 0.138, 0.1398, 0.1386", \
+          "0.1704, 0.1704, 0.1716, 0.1722, 0.171, 0.1734", \
+          "0.2322, 0.2322, 0.231, 0.231, 0.2316, 0.231", \
+          "0.3738, 0.3738, 0.3732, 0.3738, 0.3732, 0.3732", \
+          "0.4518, 0.4518, 0.4512, 0.4518, 0.4512, 0.4518", \
+          "0.531, 0.531, 0.5316, 0.531, 0.531, 0.5316");
+      }
+    }
+    timing() {
+      related_pin : "OEN";
+      timing_type : three_state_disable;
+      timing_sense : negative_unate;
+      cell_rise(delay_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0.81801, 0.875159, 0.960552, 1.09692, 1.18016, 1.33249");
+      }
+      rise_transition(delay_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("0.81801, 0.875159, 0.960552, 1.09692, 1.18016, 1.33249");
+      }
+      cell_fall(delay_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("1.45459, 1.51654, 1.59578, 1.71923, 1.79743, 1.94063");
+      }
+      fall_transition(delay_template_6x1) {
+        index_1 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ("1.45459, 1.51654, 1.59578, 1.71923, 1.79743, 1.94063");
+      }
+    }
+    internal_power() {
+      related_pin : "DO";
+      rise_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "13.3693, 13.5644, 13.9316, 15.0257, 15.9455, 17.6391", \
+          "13.4393, 13.6138, 13.9801, 15.1337, 15.9931, 17.7663", \
+          "13.6731, 13.7663, 14.197, 15.3749, 16.1769, 17.9779", \
+          "14.2581, 14.3969, 14.8963, 15.9839, 16.8333, 18.6771", \
+          "14.6069, 14.783, 15.2305, 16.3535, 17.2164, 19.0077", \
+          "14.982, 15.1434, 15.6147, 16.7487, 17.5969, 19.4049");
+      }
+      fall_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "28.3762, 28.4342, 28.909, 29.7262, 30.1816, 31.4312", \
+          "27.2939, 27.3651, 27.8909, 28.7373, 29.1882, 30.4528", \
+          "25.992, 26.0566, 26.4998, 27.4051, 27.8885, 29.294", \
+          "23.9139, 24.017, 24.5617, 25.4326, 26.0431, 27.546", \
+          "23.3001, 23.4155, 23.9275, 24.942, 25.5264, 27.0459", \
+          "22.9255, 23.0457, 23.5486, 24.5807, 25.2163, 26.7967");
+      }
+    }
+    internal_power() {
+      related_pin : "OEN";
+      rise_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "17.3333, 17.3941, 17.4597, 17.8279, 17.9936, 18.523", \
+          "17.5137, 17.529, 17.6008, 17.8267, 18.0859, 18.5803", \
+          "17.6438, 17.6853, 17.7615, 18.0874, 18.3221, 18.7593", \
+          "18.2105, 18.2195, 18.3238, 18.5898, 18.834, 19.2867", \
+          "18.5235, 18.5293, 18.6632, 18.9447, 19.1604, 19.6392", \
+          "18.8792, 18.8846, 18.9951, 19.2784, 19.52, 19.9748");
+      }
+      fall_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "12.2986, 12.3559, 12.5851, 12.9559, 13.2653, 13.9614", \
+          "12.4284, 12.4803, 12.622, 13.1296, 13.3974, 14.0072", \
+          "12.6672, 12.7027, 12.7732, 13.2912, 13.5598, 14.158", \
+          "13.0905, 13.1278, 13.2864, 13.7133, 13.9951, 14.6463", \
+          "13.3787, 13.4146, 13.5793, 14.0081, 14.2874, 14.9475", \
+          "13.6474, 13.684, 13.8724, 14.3059, 14.6134, 15.2231");
+      }
+    }
+  }
+}
+
+/* --------------- *
+ * Design : PADOUT *
+ * --------------- */
+cell (PADOUT) {
+ pad_cell : true;
+area : 12000;
+  cell_leakage_power : 1.00545;
+  pin(DO)  {
+    direction : input;
+    capacitance : 0.25215;
+    rise_capacitance : 0.251005;
+    fall_capacitance : 0.25215;
+  }
+  pin(YPAD)  {
+ is_pad : true;
+    direction : output;
+ drive_current : 2.0;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 9.44634;
+    function : "DO";
+    timing() {
+      related_pin : "DO";
+      timing_sense : positive_unate;
+      cell_rise(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.592524, 0.620346, 0.641214, 0.663974, 0.66899, 0.671428", \
+          "0.646934, 0.674846, 0.695651, 0.719378, 0.724583, 0.727539", \
+          "0.725028, 0.753077, 0.774274, 0.797934, 0.803839, 0.807345", \
+          "0.888457, 0.916215, 0.937559, 0.96169, 0.967811, 0.971692", \
+          "0.969026, 0.996925, 1.01844, 1.04264, 1.04857, 1.05254", \
+          "1.04685, 1.07464, 1.09595, 1.12035, 1.12647, 1.13049");
+      }
+      rise_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.1746, 0.1764, 0.1722, 0.1752, 0.1788, 0.18", \
+          "0.2214, 0.2208, 0.222, 0.222, 0.2226, 0.2238", \
+          "0.297, 0.2976, 0.2988, 0.2982, 0.2982, 0.2994", \
+          "0.4812, 0.4812, 0.4818, 0.4818, 0.4818, 0.4818", \
+          "0.582, 0.582, 0.582, 0.582, 0.5826, 0.582", \
+          "0.6828, 0.6828, 0.6828, 0.6828, 0.6834, 0.6828");
+      }
+      cell_fall(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.58905, 0.639829, 0.693677, 0.779719, 0.834977, 0.933865", \
+          "0.626019, 0.676719, 0.731176, 0.817648, 0.872564, 0.971599", \
+          "0.683105, 0.733828, 0.788131, 0.87412, 0.929892, 1.02901", \
+          "0.805329, 0.856124, 0.910615, 0.996383, 1.05249, 1.15196", \
+          "0.866553, 0.917379, 0.971785, 1.05805, 1.11418, 1.21361", \
+          "0.925606, 0.976455, 1.03104, 1.11719, 1.17367, 1.27317");
+      }
+      fall_transition(delay_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "0.2256, 0.2244, 0.2226, 0.2202, 0.2154, 0.2088", \
+          "0.2508, 0.249, 0.2472, 0.2454, 0.2394, 0.2352", \
+          "0.2952, 0.2946, 0.2934, 0.2898, 0.285, 0.282", \
+          "0.4092, 0.4086, 0.408, 0.4056, 0.4032, 0.3996", \
+          "0.477, 0.4758, 0.4764, 0.474, 0.4716, 0.4698", \
+          "0.5496, 0.549, 0.5484, 0.5466, 0.546, 0.5424");
+      }
+    }
+    internal_power() {
+      related_pin : "DO";
+      rise_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "13.0671, 13.2376, 13.6266, 14.7085, 15.6789, 17.5639", \
+          "13.197, 13.3591, 13.775, 14.9332, 15.7929, 17.6686", \
+          "13.5471, 13.6264, 14.0609, 15.2442, 16.0641, 17.8557", \
+          "14.2296, 14.3675, 14.8685, 15.9554, 16.8048, 18.658", \
+          "14.601, 14.7819, 15.2319, 16.354, 17.2162, 19.0078", \
+          "14.9845, 15.1423, 15.616, 16.7493, 17.5969, 19.4049");
+      }
+      fall_power(energy_template_6x6) {
+        index_1 ("0.1, 0.5, 1.2, 3, 4, 5");
+        index_2 ("0.06, 0.24, 0.48, 0.9, 1.2, 1.8");
+        values ( \
+          "28.1289, 28.1433, 28.6709, 29.442, 29.9409, 31.2284", \
+          "27.1056, 27.1221, 27.6502, 28.5, 28.9893, 30.2259", \
+          "25.8458, 25.9108, 26.3779, 27.2622, 27.7613, 29.1385", \
+          "23.871, 23.9753, 24.5218, 25.3883, 25.9884, 27.506", \
+          "23.2737, 23.3975, 23.9098, 24.9162, 25.5, 27.0176", \
+          "22.9163, 23.0373, 23.5396, 24.5726, 25.2084, 26.7964");
+      }
+    }
+  }
+}
+
+/* --------------- *
+ * Design : TBUFX1 *
+ * --------------- */
+cell (TBUFX1) {
+area : 160;
+  cell_leakage_power : 0.0371798;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0265851;
+    rise_capacitance : 0.0263529;
+    fall_capacitance : 0.0265851;
+  }
+  pin(EN)  {
+    direction : input;
+    capacitance : 0.0188377;
+    rise_capacitance : 0.0185919;
+    fall_capacitance : 0.0188377;
+    internal_power() {
+      rise_power(passive_energy_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0, 0, 0, 0, 0");
+      }
+      fall_power(passive_energy_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.140165, 0.17407, 0.245999, 0.301738, 0.494453");
+      }
+    }
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0.00361644;
+    rise_capacitance : 0.00361644;
+    fall_capacitance : 0.00360475;
+    max_capacitance : 0.419492;
+    function : "(!A)";
+    three_state : "(!EN)";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.0186047, 0.0436048, 0.0836047, 0.203605, 0.403605");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.076828, 0.084201, 0.094381, 0.097456, 0.094047", \
+          "0.117173, 0.126665, 0.143616, 0.151084, 0.160578", \
+          "0.177971, 0.188904, 0.210149, 0.222756, 0.248764", \
+          "0.359557, 0.365852, 0.388987, 0.407904, 0.459819", \
+          "0.652641, 0.657987, 0.677926, 0.695959, 0.76241");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.0186047, 0.0436048, 0.0836047, 0.203605, 0.403605");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0858, 0.1032, 0.15, 0.1872, 0.3", \
+          "0.1362, 0.1464, 0.1926, 0.2268, 0.3498", \
+          "0.2196, 0.225, 0.258, 0.2898, 0.4146", \
+          "0.4692, 0.4692, 0.4824, 0.504, 0.6078", \
+          "0.888, 0.888, 0.8904, 0.8988, 0.9612");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.0186164, 0.0436164, 0.0836164, 0.203616, 0.403616");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.099288, 0.115332, 0.143742, 0.166674, 0.233705", \
+          "0.149629, 0.162947, 0.198575, 0.224753, 0.305215", \
+          "0.222986, 0.237122, 0.274689, 0.305122, 0.399352", \
+          "0.442935, 0.455158, 0.491737, 0.52437, 0.633345", \
+          "0.809218, 0.820335, 0.852257, 0.880571, 0.991413");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.0186164, 0.0436164, 0.0836164, 0.203616, 0.403616");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0984, 0.1188, 0.1626, 0.1962, 0.3048", \
+          "0.168, 0.1788, 0.2172, 0.2496, 0.363", \
+          "0.276, 0.2802, 0.3078, 0.3372, 0.447", \
+          "0.6072, 0.6072, 0.6156, 0.6306, 0.7128", \
+          "1.158, 1.158, 1.158, 1.1634, 1.2036");
+      }
+    }
+    timing() {
+      related_pin : "EN";
+      timing_sense : positive_unate;
+      timing_type : three_state_enable;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.0186164, 0.0436164, 0.0836164, 0.203616, 0.403616");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.095408, 0.112526, 0.130521, 0.133493, 0.131664", \
+          "0.145862, 0.161494, 0.181367, 0.189341, 0.196055", \
+          "0.217972, 0.23754, 0.255784, 0.265788, 0.276245", \
+          "0.436765, 0.456917, 0.473495, 0.480871, 0.501758", \
+          "0.802871, 0.823056, 0.839344, 0.847499, 0.864799");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.0186164, 0.0436164, 0.0836164, 0.203616, 0.403616");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1194, 0.105, 0.1098, 0.1134, 0.1314", \
+          "0.1734, 0.1686, 0.1674, 0.1728, 0.183", \
+          "0.2772, 0.2778, 0.2712, 0.2724, 0.2796", \
+          "0.6072, 0.6072, 0.603, 0.6006, 0.6012", \
+          "1.158, 1.1586, 1.1586, 1.155, 1.1508");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.0186047, 0.0436048, 0.0836047, 0.203605, 0.403605");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.03734, 0.02978, -0.014201, -0.061021, -0.249089", \
+          "0.068572, 0.083281, 0.071454, 0.044672, -0.087179", \
+          "0.12057, 0.145265, 0.160477, 0.153379, 0.079668", \
+          "0.296755, 0.321408, 0.369814, 0.397731, 0.412678", \
+          "0.590795, 0.613491, 0.662734, 0.6994, 0.804899");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.0186047, 0.0436048, 0.0836047, 0.203605, 0.403605");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.051481, 0.072, 0.1158, 0.1422, 0.2172", \
+          "0.1062, 0.1314, 0.1824, 0.213, 0.306", \
+          "0.195, 0.2124, 0.2682, 0.303, 0.4176", \
+          "0.4632, 0.4644, 0.5022, 0.5442, 0.6804", \
+          "0.888, 0.888, 0.8934, 0.9162, 1.0566");
+      }
+    }
+    timing() {
+      related_pin : "EN";
+      timing_type : three_state_disable;
+      timing_sense : negative_unate;
+      cell_rise(delay_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.052455, 0.087424, 0.157364, 0.209818, 0.384667");
+      }
+      rise_transition(delay_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.052455, 0.087424, 0.157364, 0.209818, 0.384667");
+      }
+      cell_fall(delay_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.09424, 0.135387, 0.192558, 0.226641, 0.331784");
+      }
+      fall_transition(delay_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.09424, 0.135387, 0.192558, 0.226641, 0.331784");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.0186047, 0.0436048, 0.0836047, 0.203605, 0.403605");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.103192, 0.080326, 0.014281, 0.1047, 0.434172", \
+          "0.101882, 0.088224, 0.010258, 0.07087, 0.379852", \
+          "0.100717, 0.094126, 0.030307, 0.036591, 0.315426", \
+          "0.097476, 0.095575, 0.058042, 0.01135, 0.201919", \
+          "0.096551, 0.097929, 0.074327, 0.042678, 0.113631");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.0186164, 0.0436164, 0.0836164, 0.203616, 0.403616");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.44505, 0.465527, 0.571316, 0.668858, 1.01007", \
+          "0.44847, 0.460463, 0.549299, 0.636232, 0.961654", \
+          "0.450577, 0.458122, 0.527809, 0.602928, 0.902677", \
+          "0.452697, 0.454258, 0.499982, 0.55395, 0.792169", \
+          "0.453817, 0.452771, 0.482423, 0.520229, 0.700471");
+      }
+    }
+    internal_power() {
+      related_pin : "EN";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.0186164, 0.0436164, 0.0836164, 0.203616, 0.403616");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.149767, 0.168073, 0.236399, 0.289922, 0.480324", \
+          "0.149165, 0.170713, 0.236598, 0.290711, 0.481841", \
+          "0.151761, 0.172631, 0.237164, 0.292365, 0.483494", \
+          "0.153272, 0.175021, 0.239532, 0.294092, 0.485565", \
+          "0.155067, 0.176832, 0.241418, 0.296884, 0.487316");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.0186047, 0.0436048, 0.0836047, 0.203605, 0.403605");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.027381, 0.000426, 0.073369, 0.134651, 0.337276", \
+          "0.027559, 0.006006, 0.067777, 0.12626, 0.327413", \
+          "0.027984, 0.004733, 0.064516, 0.122395, 0.321035", \
+          "0.027156, 0.006521, 0.062465, 0.118152, 0.313884", \
+          "0.026177, 0.004992, 0.062272, 0.116596, 0.309454");
+      }
+    }
+  }
+}
+
+/* --------------- *
+ * Design : TBUFX2 *
+ * --------------- */
+cell (TBUFX2) {
+area : 224;
+  cell_leakage_power : 0.0568702;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0531702;
+    rise_capacitance : 0.0527065;
+    fall_capacitance : 0.0531702;
+  }
+  pin(EN)  {
+    direction : input;
+    capacitance : 0.0383233;
+    rise_capacitance : 0.0378359;
+    fall_capacitance : 0.0383233;
+    internal_power() {
+      rise_power(passive_energy_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0, 0, 0, 0, 0");
+      }
+      fall_power(passive_energy_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.28509, 0.355139, 0.498984, 0.611731, 1.00214");
+      }
+    }
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0.00723131;
+    rise_capacitance : 0.00723131;
+    fall_capacitance : 0.00721944;
+    max_capacitance : 0.839062;
+    function : "(!A)";
+    three_state : "(!EN)";
+    timing() {
+      related_pin : "A";
+      timing_sense : negative_unate;
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.0372194, 0.0872194, 0.167219, 0.407219, 0.807219");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.076715, 0.084118, 0.094321, 0.097406, 0.094015", \
+          "0.11709, 0.126595, 0.143561, 0.151055, 0.160547", \
+          "0.177904, 0.188852, 0.210104, 0.222716, 0.248739", \
+          "0.359531, 0.365827, 0.388963, 0.407884, 0.459801", \
+          "0.652626, 0.657974, 0.677915, 0.695948, 0.762399");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.0372194, 0.0872194, 0.167219, 0.407219, 0.807219");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0858, 0.1032, 0.15, 0.1872, 0.3", \
+          "0.1362, 0.1464, 0.1932, 0.2274, 0.3498", \
+          "0.2196, 0.225, 0.258, 0.2898, 0.4152", \
+          "0.4692, 0.4692, 0.4824, 0.504, 0.6078", \
+          "0.888, 0.888, 0.8904, 0.8988, 0.9612");
+      }
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.0372313, 0.0872313, 0.167231, 0.407231, 0.807231");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.099401, 0.115389, 0.143753, 0.166679, 0.233701", \
+          "0.149795, 0.163017, 0.198605, 0.224773, 0.305224", \
+          "0.22315, 0.237211, 0.274733, 0.305156, 0.399374", \
+          "0.443122, 0.455261, 0.491804, 0.524427, 0.633382", \
+          "0.809407, 0.820445, 0.852334, 0.88064, 0.991465");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.0372313, 0.0872313, 0.167231, 0.407231, 0.807231");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0984, 0.1188, 0.1626, 0.1962, 0.3048", \
+          "0.168, 0.1788, 0.2178, 0.2496, 0.363", \
+          "0.276, 0.2802, 0.3078, 0.3378, 0.447", \
+          "0.6072, 0.6072, 0.6156, 0.6306, 0.7128", \
+          "1.158, 1.158, 1.1586, 1.1634, 1.2042");
+      }
+    }
+    timing() {
+      related_pin : "EN";
+      timing_sense : positive_unate;
+      timing_type : three_state_enable;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.0372313, 0.0872313, 0.167231, 0.407231, 0.807231");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.096136, 0.112637, 0.133551, 0.137455, 0.139567", \
+          "0.144511, 0.162967, 0.184621, 0.193597, 0.204492", \
+          "0.218832, 0.234844, 0.259985, 0.270485, 0.2851", \
+          "0.437616, 0.458768, 0.476618, 0.485323, 0.510655", \
+          "0.803742, 0.824872, 0.842612, 0.851602, 0.873426");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.0372313, 0.0872313, 0.167231, 0.407231, 0.807231");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.12, 0.1044, 0.1194, 0.1146, 0.1314", \
+          "0.171, 0.1698, 0.168, 0.1716, 0.1842", \
+          "0.2772, 0.276, 0.2724, 0.2724, 0.2814", \
+          "0.6072, 0.6072, 0.603, 0.6006, 0.6018", \
+          "1.158, 1.158, 1.1586, 1.155, 1.1514");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.0372194, 0.0872194, 0.167219, 0.407219, 0.807219");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.037439, 0.028422, -0.01399, -0.060808, -0.248974", \
+          "0.068812, 0.083575, 0.071764, 0.045145, -0.086873", \
+          "0.120654, 0.145382, 0.160899, 0.15403, 0.08029", \
+          "0.296766, 0.321403, 0.369785, 0.397677, 0.412731", \
+          "0.589755, 0.613454, 0.662091, 0.69935, 0.80482");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.0372194, 0.0872194, 0.167219, 0.407219, 0.807219");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.051386, 0.0774, 0.1164, 0.1422, 0.2172", \
+          "0.1056, 0.1308, 0.1818, 0.2124, 0.3066", \
+          "0.1944, 0.2118, 0.2676, 0.3024, 0.417", \
+          "0.4632, 0.4644, 0.5016, 0.543, 0.6786", \
+          "0.888, 0.888, 0.8934, 0.9162, 1.0554");
+      }
+    }
+    timing() {
+      related_pin : "EN";
+      timing_type : three_state_disable;
+      timing_sense : negative_unate;
+      cell_rise(delay_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.052455, 0.087424, 0.157364, 0.209818, 0.384667");
+      }
+      rise_transition(delay_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.052455, 0.087424, 0.157364, 0.209818, 0.384667");
+      }
+      cell_fall(delay_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.093579, 0.13382, 0.187658, 0.221575, 0.321724");
+      }
+      fall_transition(delay_template_5x1) {
+        index_1 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ("0.093579, 0.13382, 0.187658, 0.221575, 0.321724");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      fall_power(energy_template_5x5) {
+        index_1 ("0.0372194, 0.0872194, 0.167219, 0.407219, 0.807219");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.206392, 0.160627, 0.028715, 0.20956, 0.868512", \
+          "0.203794, 0.176425, 0.020593, 0.141853, 0.759855", \
+          "0.201451, 0.188242, 0.060548, 0.073254, 0.630942", \
+          "0.194957, 0.191149, 0.116051, 0.02267, 0.403868", \
+          "0.193114, 0.195856, 0.14864, 0.08534, 0.227286");
+      }
+      rise_power(energy_template_5x5) {
+        index_1 ("0.0372313, 0.0872313, 0.167231, 0.407231, 0.807231");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.890081, 0.931027, 1.14258, 1.33767, 2.0201", \
+          "0.896904, 0.920914, 1.09856, 1.27242, 1.92327", \
+          "0.901126, 0.916224, 1.0556, 1.20582, 1.80532", \
+          "0.90537, 0.908499, 0.999949, 1.10789, 1.58431", \
+          "0.907609, 0.905515, 0.964827, 1.04042, 1.4009");
+      }
+    }
+    internal_power() {
+      related_pin : "EN";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.0372313, 0.0872313, 0.167231, 0.407231, 0.807231");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.296874, 0.334665, 0.465475, 0.581634, 0.965345", \
+          "0.297407, 0.339405, 0.472578, 0.583168, 0.968954", \
+          "0.300652, 0.336319, 0.474833, 0.585379, 0.972432", \
+          "0.302877, 0.346556, 0.476963, 0.588082, 0.975624", \
+          "0.305037, 0.348694, 0.479497, 0.591584, 0.97741");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.0372194, 0.0872194, 0.167219, 0.407219, 0.807219");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.056852, 0.005349, 0.147184, 0.271491, 0.68152", \
+          "0.057367, 0.013421, 0.135852, 0.254239, 0.661972", \
+          "0.058412, 0.01095, 0.129586, 0.246448, 0.648837", \
+          "0.056676, 0.014518, 0.124877, 0.237779, 0.634648", \
+          "0.056276, 0.011181, 0.125738, 0.234929, 0.625799");
+      }
+    }
+  }
+}
+
+/* ---------------- *
+ * Design : XNOR2X1 *
+ * ---------------- */
+cell (XNOR2X1) {
+area : 224;
+  cell_leakage_power : 0.0845795;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0435905;
+    rise_capacitance : 0.0435905;
+    fall_capacitance : 0.0435166;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0524659;
+    rise_capacitance : 0.0522323;
+    fall_capacitance : 0.0524659;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.413242;
+    function : "(!(A^B))";
+    timing() {
+      related_pin : "A";
+      timing_sense : non_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.122672, 0.134136, 0.157188, 0.16667, 0.178502", \
+          "0.166322, 0.179567, 0.201718, 0.215511, 0.229878", \
+          "0.237559, 0.251304, 0.273205, 0.287008, 0.304247", \
+          "0.453679, 0.468398, 0.486993, 0.497346, 0.525865", \
+          "0.817445, 0.837677, 0.854341, 0.863129, 0.884568");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1092, 0.102, 0.1026, 0.1074, 0.1074", \
+          "0.1758, 0.1734, 0.1668, 0.1692, 0.177", \
+          "0.2862, 0.285, 0.276, 0.2742, 0.282", \
+          "0.6156, 0.6162, 0.6102, 0.6072, 0.606", \
+          "1.167, 1.167, 1.167, 1.1628, 1.1574");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.12246, 0.144057, 0.198046, 0.224615, 0.297657", \
+          "0.158576, 0.18501, 0.236012, 0.265766, 0.34284", \
+          "0.215829, 0.243984, 0.292198, 0.32574, 0.405693", \
+          "0.388115, 0.419023, 0.46212, 0.494234, 0.584898", \
+          "0.67894, 0.709262, 0.754666, 0.784086, 0.870071");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0936, 0.0882, 0.0846, 0.087, 0.0918", \
+          "0.147, 0.141, 0.1356, 0.1362, 0.1422", \
+          "0.228, 0.2262, 0.2178, 0.2154, 0.2202", \
+          "0.477, 0.477, 0.4716, 0.468, 0.4674", \
+          "0.8964, 0.8964, 0.897, 0.8922, 0.8868");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : non_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.148249, 0.161582, 0.180211, 0.191581, 0.199407", \
+          "0.195238, 0.208939, 0.228777, 0.23772, 0.247217", \
+          "0.267251, 0.283307, 0.30294, 0.310666, 0.32178", \
+          "0.487144, 0.50224, 0.525354, 0.527584, 0.542364", \
+          "0.852816, 0.868304, 0.891146, 0.896413, 0.907998");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.108, 0.111, 0.1146, 0.123, 0.1296", \
+          "0.177, 0.1776, 0.1788, 0.183, 0.189", \
+          "0.2856, 0.285, 0.288, 0.2886, 0.294", \
+          "0.6156, 0.6156, 0.6156, 0.6162, 0.6198", \
+          "1.1664, 1.1664, 1.1664, 1.1664, 1.1676");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.138356, 0.164181, 0.203649, 0.229776, 0.296225", \
+          "0.178593, 0.203332, 0.242689, 0.268427, 0.338395", \
+          "0.239262, 0.264316, 0.303597, 0.329232, 0.399082", \
+          "0.415903, 0.444943, 0.480073, 0.50553, 0.575885", \
+          "0.709428, 0.737467, 0.776009, 0.801086, 0.868476");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0948, 0.0978, 0.105, 0.1056, 0.1128", \
+          "0.1458, 0.1458, 0.15, 0.1494, 0.1608", \
+          "0.2268, 0.2274, 0.2298, 0.2298, 0.2352", \
+          "0.4758, 0.4758, 0.4764, 0.4776, 0.4812", \
+          "0.8958, 0.8958, 0.8958, 0.8958, 0.8976");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.176339, 0.212627, 0.343655, 0.46314, 0.874668", \
+          "0.166374, 0.212124, 0.344345, 0.460558, 0.858811", \
+          "0.157822, 0.207839, 0.341546, 0.456905, 0.850535", \
+          "0.148254, 0.199135, 0.334008, 0.449413, 0.845296", \
+          "0.1422, 0.190283, 0.327241, 0.440273, 0.838154");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.543678, 0.594587, 0.74769, 0.862867, 1.27447", \
+          "0.522047, 0.586707, 0.746389, 0.860343, 1.26799", \
+          "0.514338, 0.580995, 0.735615, 0.859754, 1.2608", \
+          "0.506836, 0.568046, 0.724235, 0.843447, 1.25031", \
+          "0.501893, 0.562084, 0.713302, 0.832769, 1.23805");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.220702, 0.253713, 0.395656, 0.507623, 0.91275", \
+          "0.221657, 0.259022, 0.394993, 0.504711, 0.905113", \
+          "0.222337, 0.260683, 0.392106, 0.505526, 0.900384", \
+          "0.222792, 0.26222, 0.398756, 0.506637, 0.895323", \
+          "0.222654, 0.262338, 0.398927, 0.505337, 0.89301");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.60089, 0.647689, 0.79509, 0.913318, 1.32612", \
+          "0.600574, 0.652003, 0.793614, 0.917726, 1.32106", \
+          "0.602594, 0.653287, 0.800245, 0.912524, 1.31313", \
+          "0.604516, 0.660143, 0.79977, 0.915825, 1.31046", \
+          "0.603772, 0.661082, 0.801002, 0.916516, 1.30907");
+      }
+    }
+  }
+}
+
+/* --------------- *
+ * Design : XOR2X1 *
+ * --------------- */
+cell (XOR2X1) {
+area : 224;
+  cell_leakage_power : 0.0854615;
+  pin(A)  {
+    direction : input;
+    capacitance : 0.0435889;
+    rise_capacitance : 0.0435889;
+    fall_capacitance : 0.0434863;
+  }
+  pin(B)  {
+    direction : input;
+    capacitance : 0.0524749;
+    rise_capacitance : 0.0522388;
+    fall_capacitance : 0.0524749;
+  }
+  pin(Y)  {
+    direction : output;
+    capacitance : 0;
+    rise_capacitance : 0;
+    fall_capacitance : 0;
+    max_capacitance : 0.413518;
+    function : "(A^B)";
+    timing() {
+      related_pin : "A";
+      timing_sense : non_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.122933, 0.134344, 0.157356, 0.166768, 0.178571", \
+          "0.166602, 0.179836, 0.201919, 0.215673, 0.229997", \
+          "0.237832, 0.251535, 0.273395, 0.287252, 0.304389", \
+          "0.453374, 0.46857, 0.487147, 0.497486, 0.525974", \
+          "0.817112, 0.837843, 0.854446, 0.86324, 0.884739");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1092, 0.1026, 0.1032, 0.108, 0.1074", \
+          "0.1758, 0.1734, 0.1668, 0.1692, 0.177", \
+          "0.2856, 0.285, 0.276, 0.2742, 0.2814", \
+          "0.6156, 0.6162, 0.6102, 0.6072, 0.606", \
+          "1.167, 1.167, 1.167, 1.1628, 1.1574");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.122337, 0.146792, 0.198101, 0.224566, 0.297637", \
+          "0.15842, 0.18488, 0.236171, 0.26573, 0.34282", \
+          "0.215693, 0.243863, 0.292092, 0.325723, 0.405691", \
+          "0.38858, 0.419048, 0.46219, 0.494294, 0.584956", \
+          "0.679087, 0.70933, 0.754762, 0.784192, 0.870189");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.0936, 0.0882, 0.0846, 0.087, 0.0918", \
+          "0.147, 0.141, 0.1338, 0.1362, 0.1428", \
+          "0.2286, 0.2262, 0.2178, 0.2154, 0.2202", \
+          "0.477, 0.477, 0.4716, 0.4686, 0.468", \
+          "0.897, 0.8964, 0.897, 0.8928, 0.8874");
+      }
+    }
+    timing() {
+      related_pin : "B";
+      timing_sense : non_unate;
+      cell_rise(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.144619, 0.156874, 0.176484, 0.186124, 0.196814", \
+          "0.191768, 0.205607, 0.225107, 0.23382, 0.244483", \
+          "0.263881, 0.28074, 0.299368, 0.307457, 0.318682", \
+          "0.483447, 0.505972, 0.521648, 0.527949, 0.539474", \
+          "0.849092, 0.872211, 0.887562, 0.894031, 0.90509");
+      }
+      rise_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.1104, 0.1134, 0.1164, 0.1236, 0.132", \
+          "0.1776, 0.1782, 0.1788, 0.1818, 0.1926", \
+          "0.285, 0.285, 0.288, 0.2886, 0.294", \
+          "0.6144, 0.6144, 0.615, 0.6156, 0.6186", \
+          "1.1652, 1.1652, 1.1652, 1.1658, 1.1664");
+      }
+      cell_fall(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.145193, 0.167445, 0.207025, 0.23445, 0.300689", \
+          "0.183018, 0.208158, 0.246245, 0.272205, 0.341196", \
+          "0.24309, 0.268668, 0.307056, 0.331972, 0.401932", \
+          "0.419484, 0.444925, 0.483225, 0.508163, 0.578534", \
+          "0.712649, 0.737261, 0.77892, 0.803399, 0.871081");
+      }
+      fall_transition(delay_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.093, 0.0954, 0.1008, 0.102, 0.1164", \
+          "0.1446, 0.1446, 0.1476, 0.1482, 0.1602", \
+          "0.2268, 0.2268, 0.2292, 0.231, 0.2352", \
+          "0.4764, 0.477, 0.477, 0.4782, 0.4818", \
+          "0.897, 0.897, 0.897, 0.897, 0.8988");
+      }
+    }
+    internal_power() {
+      related_pin : "A";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.16223, 0.19924, 0.330891, 0.450355, 0.85608", \
+          "0.15295, 0.198387, 0.331072, 0.447385, 0.845471", \
+          "0.144331, 0.194114, 0.328312, 0.443791, 0.837433", \
+          "0.133885, 0.18547, 0.320769, 0.436208, 0.832024", \
+          "0.128526, 0.177405, 0.314082, 0.427065, 0.824831");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.559534, 0.610414, 0.765392, 0.879553, 1.29246", \
+          "0.540068, 0.604521, 0.758941, 0.877728, 1.28568", \
+          "0.532821, 0.599493, 0.753499, 0.877692, 1.2785", \
+          "0.523761, 0.587193, 0.743433, 0.862109, 1.269", \
+          "0.519548, 0.58142, 0.732494, 0.851847, 1.25709");
+      }
+    }
+    internal_power() {
+      related_pin : "B";
+      rise_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.119288, 0.152651, 0.292972, 0.406205, 0.812813", \
+          "0.121522, 0.153145, 0.29344, 0.402596, 0.804257", \
+          "0.122482, 0.154474, 0.290082, 0.404108, 0.799275", \
+          "0.123254, 0.155663, 0.297868, 0.407272, 0.794032", \
+          "0.123314, 0.156069, 0.298135, 0.407284, 0.791857");
+      }
+      fall_power(energy_template_5x5) {
+        index_1 ("0.015, 0.04, 0.08, 0.2, 0.4");
+        index_2 ("0.06, 0.18, 0.42, 0.6, 1.2");
+        values ( \
+          "0.696383, 0.740188, 0.886317, 1.00972, 1.42237", \
+          "0.698037, 0.748875, 0.88776, 1.01308, 1.41685", \
+          "0.699985, 0.750658, 0.895898, 1.0131, 1.40899", \
+          "0.701806, 0.752479, 0.895906, 1.01155, 1.40644", \
+          "0.700092, 0.753668, 0.896398, 1.01464, 1.40504");
+      }
+    }
+  }
+}
+
+
+cell (PADFC) { area : 27000;  dont_touch : true; }
+cell (PADNC) { area : 27000;  dont_touch : true; }
+cell (PADVDD) { area : 27000; dont_touch : true; }
+cell (PADGND) { area : 27000; dont_touch : true; }
+}
diff --git a/test/osu350/README.md b/test/osu350/README.md
new file mode 100644
index 0000000..e99ef99
--- /dev/null
+++ b/test/osu350/README.md
@@ -0,0 +1,4 @@
+This is the 0.35um SCMOS MOSIS library developed by Oklahoma State
+University back in early 2000.  It is utilized as a test case to help
+develop the characterizater.
+
diff --git a/test/osu350/fetch_spice.sh b/test/osu350/fetch_spice.sh
new file mode 100755
index 0000000..c0a3f4b
--- /dev/null
+++ b/test/osu350/fetch_spice.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+TARGET_DIR='osu350_spice_temp'
+SOURCE='https://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/latest/cadence/lib/ami035/signalstorm/osu035_stdcells.sp'
+TEMP_SP_FILENAME='osu035_stdcells.sp.temp'
+
+mkdir -p $TARGET_DIR
+pushd $TARGET_DIR > /dev/null
+
+# Download source
+curl -s $SOURCE > $TEMP_SP_FILENAME
+
+# Split into separate files
+awk -v RS= '{print > ("temp_cell_" NR ".sp.temp")}' $TEMP_SP_FILENAME
+rm $TEMP_SP_FILENAME
+
+# Correct cell filenames
+for TEMP_CELL_FILENAME in `ls *.sp.temp`
+do
+    decl=$(head -n1 $TEMP_CELL_FILENAME)
+    decl_words=($decl)
+    cell_name=${decl_words[1]}
+    mv $TEMP_CELL_FILENAME $cell_name.sp
+done
+
+popd > /dev/null
\ No newline at end of file
diff --git a/test/osu350/model.sp b/test/osu350/model.sp
new file mode 100644
index 0000000..a5ea7ac
--- /dev/null
+++ b/test/osu350/model.sp
@@ -0,0 +1,133 @@
+* run N88Y
+* this is really a TSMC model from Mosis website
+.MODEL nfet NMOS (     LEVEL   = 49
++VERSION = 3.1            TNOM    = 27             TOX     = 7.6E-9
++XJ      = 1.5E-7         NCH     = 1.7E17         VTH0    = 0.4964448
++K1      = 0.5307769      K2      = 0.0199705      K3      = 0.2963637
++K3B     = 0.2012165      W0      = 2.836319E-6    NLX     = 2.894802E-7
++DVT0W   = 0              DVT1W   = 5.3E6          DVT2W   = -0.032
++DVT0    = 0.112017       DVT1    = 0.2453972      DVT2    = -0.171915
++U0      = 444.9381976    UA      = 2.921284E-10   UB      = 1.773281E-18
++UC      = 7.067896E-11   VSAT    = 1.130785E5     A0      = 1.1356246
++AGS     = 0.2810374      B0      = 2.844393E-7    B1      = 5E-6
++KETA    = -7.8181E-3     A1      = 0              A2      = 1
++RDSW    = 925.2701982    PRWG    = -1E-3          PRWB    = -1E-3
++WR      = 1              WINT    = 7.186965E-8    LINT    = 1.735515E-9
++XL      = -2E-8          XW      = 0              DWG     = -1.712973E-8
++DWB     = 5.851691E-9    VOFF    = -0.132935      NFACTOR = 0.5710974
++CIT     = 0              CDSC    = 8.607229E-4    CDSCD   = 0
++CDSCB   = 0              ETA0    = 2.128321E-3    ETAB    = 0
++DSUB    = 0.0257957      PCLM    = 0.6766314      PDIBLC1 = 1
++PDIBLC2 = 1.787424E-3    PDIBLCB = 0              DROUT   = 0.7873539
++PSCBE1  = 6.973485E9     PSCBE2  = 1.46235E-7     PVAG    = 0.05
++DELTA   = 0.01           MOBMOD  = 1              PRT     = 0
++UTE     = -1.5           KT1     = -0.11          KT1L    = 0
++KT2     = 0.022          UA1     = 4.31E-9        UB1     = -7.61E-18
++UC1     = -5.6E-11       AT      = 3.3E4          WL      = 0
++WLN     = 1              WW      = 0              WWN     = 1
++WWL     = 0              LL      = 0              LLN     = 1
++LW      = 0              LWN     = 1              LWL     = 0
++CAPMOD  = 2              CGDO    = 1.96E-10       CGSO    = 1.96E-10
++CGBO    = 0              CJ      = 9.276962E-4    PB      = 0.8157962
++MJ      = 0.3557696      CJSW    = 3.181055E-10   PBSW    = 0.6869149
++MJSW    = 0.1            PVTH0   = -0.0252481     PRDSW   = -96.4502805
++PK2     = -4.805372E-3   WKETA   = -7.643187E-4   LKETA   = -0.0129496      )
+* run n88y
+.MODEL pfet PMOS (     LEVEL   = 49
++VERSION = 3.1            TNOM    = 27             TOX     = 7.6E-9
++XJ      = 1.5E-7         NCH     = 1.7E17         VTH0    = -0.6636594
++K1      = 0.4564781      K2      = -0.019447      K3      = 39.382919
++K3B     = -2.8930965     W0      = 2.655585E-6    NLX     = 1.51028E-7
++DVT0W   = 0              DVT1W   = 5.3E6          DVT2W   = -0.032
++DVT0    = 1.1744581      DVT1    = 0.7631128      DVT2    = -0.1035171
++U0      = 151.3305606    UA      = 2.061211E-10   UB      = 1.823477E-18
++UC      = -8.97321E-12   VSAT    = 9.915604E4     A0      = 1.1210053
++AGS     = 0.3961954      B0      = 6.493139E-7    B1      = 4.273215E-6
++KETA    = -9.27E-3       A1      = 0              A2      = 1
++RDSW    = 2.30725E3      PRWG    = -1E-3          PRWB    = 0
++WR      = 1              WINT    = 5.962233E-8    LINT    = 4.30928E-9
++XL      = -2E-8          XW      = 0              DWG     = -1.596201E-8
++DWB     = 1.378919E-8    VOFF    = -0.15          NFACTOR = 2
++CIT     = 0              CDSC    = 6.593084E-4    CDSCD   = 0
++CDSCB   = 0              ETA0    = 0.0286461      ETAB    = 0
++DSUB    = 0.2436027      PCLM    = 4.3597508      PDIBLC1 = 7.447024E-4
++PDIBLC2 = 4.256073E-3    PDIBLCB = 0              DROUT   = 0.0120292
++PSCBE1  = 1.347622E10    PSCBE2  = 5E-9           PVAG    = 3.669793
++DELTA   = 0.01           MOBMOD  = 1              PRT     = 0
++UTE     = -1.5           KT1     = -0.11          KT1L    = 0
++KT2     = 0.022          UA1     = 4.31E-9        UB1     = -7.61E-18
++UC1     = -5.6E-11       AT      = 3.3E4          WL      = 0
++WLN     = 1              WW      = 0              WWN     = 1
++WWL     = 0              LL      = 0              LLN     = 1
++LW      = 0              LWN     = 1              LWL     = 0
++CAPMOD  = 2              CGDO    = 2.307E-10      CGSO    = 2.307E-10
++CGBO    = 0              CJ      = 1.420282E-3    PB      = 0.99
++MJ      = 0.5490877      CJSW    = 4.773605E-10   PBSW    = 0.99
++MJSW    = 0.1997417      PVTH0   = 6.58707E-3     PRDSW   = -93.5582228
++PK2     = 1.011593E-3    WKETA   = -0.0101398     LKETA   = 6.027967E-3     )
+* duplicated nfet
+.MODEL hnfet NMOS (     LEVEL   = 49
++VERSION = 3.1            TNOM    = 27             TOX     = 7.6E-9
++XJ      = 1.5E-7         NCH     = 1.7E17         VTH0    = 0.4964448
++K1      = 0.5307769      K2      = 0.0199705      K3      = 0.2963637
++K3B     = 0.2012165      W0      = 2.836319E-6    NLX     = 2.894802E-7
++DVT0W   = 0              DVT1W   = 5.3E6          DVT2W   = -0.032
++DVT0    = 0.112017       DVT1    = 0.2453972      DVT2    = -0.171915
++U0      = 444.9381976    UA      = 2.921284E-10   UB      = 1.773281E-18
++UC      = 7.067896E-11   VSAT    = 1.130785E5     A0      = 1.1356246
++AGS     = 0.2810374      B0      = 2.844393E-7    B1      = 5E-6
++KETA    = -7.8181E-3     A1      = 0              A2      = 1
++RDSW    = 925.2701982    PRWG    = -1E-3          PRWB    = -1E-3
++WR      = 1              WINT    = 7.186965E-8    LINT    = 1.735515E-9
++XL      = -2E-8          XW      = 0              DWG     = -1.712973E-8
++DWB     = 5.851691E-9    VOFF    = -0.132935      NFACTOR = 0.5710974
++CIT     = 0              CDSC    = 8.607229E-4    CDSCD   = 0
++CDSCB   = 0              ETA0    = 2.128321E-3    ETAB    = 0
++DSUB    = 0.0257957      PCLM    = 0.6766314      PDIBLC1 = 1
++PDIBLC2 = 1.787424E-3    PDIBLCB = 0              DROUT   = 0.7873539
++PSCBE1  = 6.973485E9     PSCBE2  = 1.46235E-7     PVAG    = 0.05
++DELTA   = 0.01           MOBMOD  = 1              PRT     = 0
++UTE     = -1.5           KT1     = -0.11          KT1L    = 0
++KT2     = 0.022          UA1     = 4.31E-9        UB1     = -7.61E-18
++UC1     = -5.6E-11       AT      = 3.3E4          WL      = 0
++WLN     = 1              WW      = 0              WWN     = 1
++WWL     = 0              LL      = 0              LLN     = 1
++LW      = 0              LWN     = 1              LWL     = 0
++CAPMOD  = 2              CGDO    = 1.96E-10       CGSO    = 1.96E-10
++CGBO    = 0              CJ      = 9.276962E-4    PB      = 0.8157962
++MJ      = 0.3557696      CJSW    = 3.181055E-10   PBSW    = 0.6869149
++MJSW    = 0.1            PVTH0   = -0.0252481     PRDSW   = -96.4502805
++PK2     = -4.805372E-3   WKETA   = -7.643187E-4   LKETA   = -0.0129496      )
+* duplicated
+.MODEL hpfet PMOS (     LEVEL   = 49
++VERSION = 3.1            TNOM    = 27             TOX     = 7.6E-9
++XJ      = 1.5E-7         NCH     = 1.7E17         VTH0    = -0.6636594
++K1      = 0.4564781      K2      = -0.019447      K3      = 39.382919
++K3B     = -2.8930965     W0      = 2.655585E-6    NLX     = 1.51028E-7
++DVT0W   = 0              DVT1W   = 5.3E6          DVT2W   = -0.032
++DVT0    = 1.1744581      DVT1    = 0.7631128      DVT2    = -0.1035171
++U0      = 151.3305606    UA      = 2.061211E-10   UB      = 1.823477E-18
++UC      = -8.97321E-12   VSAT    = 9.915604E4     A0      = 1.1210053
++AGS     = 0.3961954      B0      = 6.493139E-7    B1      = 4.273215E-6
++KETA    = -9.27E-3       A1      = 0              A2      = 1
++RDSW    = 2.30725E3      PRWG    = -1E-3          PRWB    = 0
++WR      = 1              WINT    = 5.962233E-8    LINT    = 4.30928E-9
++XL      = -2E-8          XW      = 0              DWG     = -1.596201E-8
++DWB     = 1.378919E-8    VOFF    = -0.15          NFACTOR = 2
++CIT     = 0              CDSC    = 6.593084E-4    CDSCD   = 0
++CDSCB   = 0              ETA0    = 0.0286461      ETAB    = 0
++DSUB    = 0.2436027      PCLM    = 4.3597508      PDIBLC1 = 7.447024E-4
++PDIBLC2 = 4.256073E-3    PDIBLCB = 0              DROUT   = 0.0120292
++PSCBE1  = 1.347622E10    PSCBE2  = 5E-9           PVAG    = 3.669793
++DELTA   = 0.01           MOBMOD  = 1              PRT     = 0
++UTE     = -1.5           KT1     = -0.11          KT1L    = 0
++KT2     = 0.022          UA1     = 4.31E-9        UB1     = -7.61E-18
++UC1     = -5.6E-11       AT      = 3.3E4          WL      = 0
++WLN     = 1              WW      = 0              WWN     = 1
++WWL     = 0              LL      = 0              LLN     = 1
++LW      = 0              LWN     = 1              LWL     = 0
++CAPMOD  = 2              CGDO    = 2.307E-10      CGSO    = 2.307E-10
++CGBO    = 0              CJ      = 1.420282E-3    PB      = 0.99
++MJ      = 0.5490877      CJSW    = 4.773605E-10   PBSW    = 0.99
++MJSW    = 0.1997417      PVTH0   = 6.58707E-3     PRDSW   = -93.5582228
++PK2     = 1.011593E-3    WKETA   = -0.0101398     LKETA   = 6.027967E-3     )
diff --git a/test/osu350/osu350.yml b/test/osu350/osu350.yml
new file mode 100644
index 0000000..c5d950b
--- /dev/null
+++ b/test/osu350/osu350.yml
@@ -0,0 +1,70 @@
+settings:
+    lib_name:           OSU350
+    cell_name_prefix:   _V1
+    cell_name_suffix:   OSU350_
+    work_dir:           work
+    export_lib:         yes
+    export_cmd:         no
+    run_simulation:     yes
+    multithreaded:      yes
+    units:
+        voltage:        V
+        capacitance:    pF
+        resistance:     kOhm
+        current:        uA
+        leakage_power:  nW
+        energy:         fJ
+        time:           ns
+    named_nodes:
+        vdd:
+            name:       VDD
+            voltage:    3.3
+        vss:
+            name:       GND
+            voltage:    0
+        pwell:
+            name:       VPW
+            voltage:    0
+        nwell:
+            name:       VNW
+            voltage:    3.3
+    logic_thresholds:
+        low:            0.2 # 20% of VDD
+        high:           0.8 # 80% of VDD
+        high_to_low:    0.5
+        low_to_high:    0.5
+    energy_measurement:
+        low_threshold:  0.01
+        high_threshold: 0.99
+        time_extent:    10
+    operating_conditions:   typical
+    temperature:            25.0
+    cell_defaults:
+        model: test/osu350/model.sp
+        slews: [0.015, 0.04, 0.08, 0.2, 0.4]
+        loads: [0.06, 0.18, 0.42, 0.6, 1.2]
+        simulation_timestep: auto
+cells:
+    INVX1:
+        netlist:    osu350_spice_temp/INVX1.sp
+        area:       128
+        inputs:     [A]
+        outputs:    ['Y']
+        functions:
+            - Y=~A
+    FAX1:
+        netlist:    osu350_spice_temp/FAX1.sp
+        area:       480
+        inputs:     [A, B, C]
+        outputs:    [YC, YS]
+        functions:
+            - YC=(A&B)|(C&(A^B))
+            - YS=A^B^C
+    HAX1:
+        netlist:    osu350_spice_temp/HAX1.sp
+        area:       320
+        inputs:     [A, B]
+        outputs:    [YC, YS]
+        functions:
+            - YC=A&B
+            - YS=A^B
\ No newline at end of file