Repository: ucb-bar/hammer (language: python)

Issue:
plugin for OpenROAD
This EDA [project](https://github.com/The-OpenROAD-Project/) seems interesting, full opensource without NDA issue.
maybe this can be a solution for public flow generating for hammer.


Discussion / hints:
Related: #396 
Any updates or plans for upgrade to the priority?
Would love to see open road integrated with hammer
I think that this will remain a very low priority issue for the main hammer devs for a while.
The current focus of hammer and its devs is in helping users build chips easier and faster than before.
I don't believe OpenROAD has been used to tape-out any chips thus far and so would primarily be useful as an academic exercise. While I do see the value in this, it seems like a lot of infrastructure that doesn't align with the current dev's priorities.

All that being said I think most of us, would be happy to review PRs or discuss implementation issues if someone else wanted to lead the charge to get OpenROAD integrated into Hammer. We would encourage anyone interested to start a discussion here.
I have tried OpenROAD for 1 time...
It has some pitfall for large designs, and only can be used with printf C++ to debug(that really sucks).
I think it needs to give a full documentation.

Fix patch:
diff --git a/src/hammer-tech/filters.py b/src/hammer-tech/filters.py
index f914e409b..d9b666df2 100644
--- a/src/hammer-tech/filters.py
+++ b/src/hammer-tech/filters.py
@@ -313,3 +313,19 @@ def sort_func(lib: "Library"):
 
         return LibraryFilter.new("power_grid_library", "Power grid library", is_file=False, filter_func=filter_func,
                                  paths_func=paths_func, sort_func=sort_func)
+
+    @property
+    def klayout_techfile_filter(self) -> LibraryFilter:
+        """
+        Select KLayout tech files for GDS streaming.
+        """
+
+        def filter_func(lib: "Library") -> bool:
+            return lib.klayout_techfile is not None
+
+        def paths_func(lib: "Library") -> List[str]:
+            assert lib.klayout_techfile is not None
+            return [lib.klayout_techfile]
+
+        return LibraryFilter.new("klayout", "GDS streaming", is_file=True, filter_func=filter_func, 
+                                 paths_func=paths_func)
diff --git a/src/hammer-tech/library_filter.py b/src/hammer-tech/library_filter.py
index 0bad7214b..14c518ec4 100644
--- a/src/hammer-tech/library_filter.py
+++ b/src/hammer-tech/library_filter.py
@@ -32,6 +32,8 @@ def verilog_sim(self) -> Optional[str]: pass
         def spice_model_file(self) -> Optional[SpiceModelFile]: pass
         @property
         def power_grid_library(self) -> Optional[str]: pass
+        @property
+        def klayout_techfile(self) -> Optional[str]: pass
 
 PathsFunctionType = Callable[["Library"], List[str]]
 
diff --git a/src/hammer-tech/schema.json b/src/hammer-tech/schema.json
index c9306c316..0b5f002fe 100644
--- a/src/hammer-tech/schema.json
+++ b/src/hammer-tech/schema.json
@@ -77,6 +77,9 @@
           "lef file" : {
             "type" : "string"
           },
+          "klayout techfile" : {
+            "type" : "string"
+          },
           "spice file" : {
             "type" : "string"
           },
@@ -353,6 +356,20 @@
                "title": "string",
                "type": "string"
              }
+          },
+          "input ports": {
+            "type": "array",
+            "items": {
+              "title": "string",
+              "type": "string"
+            }
+          },
+          "output ports": {
+            "type": "array",
+            "items": {
+              "title": "string",
+              "type": "string"
+            }
           }
         }
       }
diff --git a/src/hammer-tech/specialcells.py b/src/hammer-tech/specialcells.py
index acaa10fa3..4e7206cab 100644
--- a/src/hammer-tech/specialcells.py
+++ b/src/hammer-tech/specialcells.py
@@ -20,6 +20,8 @@ class CellType(Enum):
     StdFiller = 6
     Decap = 7
     TapCell = 8
+    Driver = 9
+    CTSBuffer = 10
 
     @classmethod
     def __mapping(cls) -> Dict[str, "CellType"]:
@@ -31,7 +33,9 @@ def __mapping(cls) -> Dict[str, "CellType"]:
             "iofiller": CellType.IOFiller,
             "stdfiller": CellType.StdFiller,
             "decap": CellType.Decap,
-            "tapcell": CellType.TapCell
+            "tapcell": CellType.TapCell,
+            "driver": CellType.Driver,
+            "ctsbuffer": CellType.CTSBuffer
         }
 
     @staticmethod
@@ -49,7 +53,9 @@ def __str__(self) -> str:
 class SpecialCell(NamedTuple('SpecialCell', [
         ('cell_type', CellType),
         ('name', List[str]),
-        ('size', Optional[List[str]])
+        ('size', Optional[List[str]]),
+        ('input_ports', Optional[List[str]]),
+        ('output_ports', Optional[List[str]])
 ])):
     """
     A SpecialCell is a technology cell used for non logic task (e.g. Tiecells,
@@ -60,12 +66,20 @@ class SpecialCell(NamedTuple('SpecialCell', [
 
     @staticmethod
     def from_setting(d: dict) -> "SpecialCell":
-        size = d['size']
+        size = d["size"]
+        input_ports = d["input_ports"]
+        output_ports = d["output_ports"]
         if size is not None:
             size = list(map(lambda x: str(x), size))
+        if input_ports is not None:
+            input_ports = list(input_ports)
+        if output_ports is not None:
+            output_ports = list(output_ports)
         # pylint: disable=missing-docstring
         return SpecialCell(
             cell_type=CellType.from_str(d["cell_type"]),
             name=list(d["name"]),
             size=size,
+            input_ports=input_ports,
+            output_ports=output_ports,
         )
diff --git a/src/hammer-vlsi/defaults.yml b/src/hammer-vlsi/defaults.yml
index 0ec948a8e..9fb6ceea2 100644
--- a/src/hammer-vlsi/defaults.yml
+++ b/src/hammer-vlsi/defaults.yml
@@ -606,6 +606,14 @@ par.inputs:
   # type: int
   gds_precision: 1000
 
+  # Optional: OpenRCX RC tech file
+  # type: Optional[str]
+  openrcx_techfile: null
+
+  # Optional: KLayout tech file
+  # type: Optional[str]
+  klayout_techfile_source: null
+
 # Place-and-route settings
 par:
   # Submission command settings
@@ -791,6 +799,10 @@ technology.core:
     # Can be a wildcard/glob
     # type: str
     tap_cell_rail_reference: null
+    # Names of the power and ground rails of the standard cells
+    std_cell_supplies:
+        power: ["VDD"]
+        ground: ["VSS"]
 
 # Specific inputs for the simulation tool.
 # These inputs will vary based on if the simulation is for RTL, post synthesis, or post PAR.
diff --git a/src/hammer-vlsi/defaults_types.yml b/src/hammer-vlsi/defaults_types.yml
index ba63b33f8..9577d2f36 100644
--- a/src/hammer-vlsi/defaults_types.yml
+++ b/src/hammer-vlsi/defaults_types.yml
@@ -278,6 +278,12 @@ par.inputs:
   # type: int
   gds_precision: int
 
+  # Path to OpenROAD *.rcx_rules file
+  openrcx_techfile: Optional[str]
+
+  # Path to KLayout techfile *.lyt file
+  klayout_techfile_source: Optional[str]
+
 # Place-and-route settings
 par:
   # Submission command settings
@@ -422,6 +428,11 @@ technology.core:
     # type: str
     tap_cell_rail_reference: Optional[str]
 
+    # Name of standard cell power/ground rails
+    std_cell_supplies:
+      power: list[str]
+      ground: list[str]
+
 # Specific inputs for the simulation tool.
 sim.inputs:
   # Top RTL module for simulation
diff --git a/src/hammer-vlsi/hammer_vlsi/vendor/def2stream.py b/src/hammer-vlsi/hammer_vlsi/vendor/def2stream.py
new file mode 100644
index 000000000..76c9ca10f
--- /dev/null
+++ b/src/hammer-vlsi/hammer_vlsi/vendor/def2stream.py
@@ -0,0 +1,191 @@
+import pya
+import re
+import json
+import copy
+import sys
+import os
+
+errors = 0
+
+# Expand layers in json
+def expand_cfg_layers(cfg):
+  layers = cfg['layers']
+  expand = [layer for layer in layers if 'layers' in layers[layer]]
+  for layer in expand:
+    for i, (name, num) in enumerate(zip(layers[layer]['names'],
+                                        layers[layer]['layers'])):
+      new_layer = copy.deepcopy(layers[layer])
+      del new_layer['names']
+      new_layer['name'] = name
+      del new_layer['layers']
+      new_layer['layer'] = num
+      layers[name] = new_layer
+    del layers[layer]
+
+def read_cfg():
+  print('INFO: Reading config file: ' + config_file)
+  with open(config_file, 'r') as f:
+    cfg = json.load(f)
+
+  expand_cfg_layers(cfg)
+  cfg = cfg['layers'] # ignore the rest
+
+  # Map gds layers & datatype to KLayout indices
+  # These are arrays for the different mask numbers
+  for layer, vals in cfg.items():
+    layer = vals['layer']
+    for key in ('opc', 'non-opc'):
+      if key not in vals:
+        continue
+      data = vals[key]
+      if isinstance(data['datatype'], int):
+        data['datatype'] = [data['datatype']] # convert to array
+      data['klayout'] = [main_layout.find_layer(layer, datatype)
+                         for datatype in data['datatype']]
+
+  return cfg
+
+#match a line like:
+# - LAYER M2 + MASK 2 + OPC RECT ( 3000 3000 ) ( 5000 5000 ) ;
+rect_pat = re.compile(r'''
+  \s*\-\ LAYER\ (?P<layer>\S+)  # The layer name
+  (?:                           # Non-capturing group
+  \s+\+\ MASK\ (?P<mask>\d+)    # Mask, None if absent
+  )?
+  (?P<opc>                      # OPC, None if absent
+  \s+\+\ OPC
+  )?
+  \s+RECT\
+   \(\ (?P<xlo>\d+)\ (?P<ylo>\d+)\ \)\   # rect lower-left pt
+  \(\ (?P<xhi>\d+)\ (?P<yhi>\d+)\ \)\ ; # rect upper-right pt
+  ''',
+                      re.VERBOSE)
+
+def read_fills(top):
+  if config_file == '':
+    print('WARNING: no fill config file specified')
+    return
+  # KLayout doesn't support FILL in DEF so we have to side load them :(
+  cfg = read_cfg()
+  in_fills = False
+  units = None
+  with open(in_def) as fp:
+    for line in fp:
+      if in_fills:
+        if re.match('END FILLS', line):
+          break # done with fills; don't care what follows
+        m = re.match(rect_pat, line)
+        if not m:
+          raise Exception('Unrecognized fill: ' + line)
+        opc_type = 'opc' if m.group('opc') else 'non-opc'
+        mask = m.group('mask')
+        if not mask: #uncolored just uses first entry
+          mask = 0
+        else:
+          mask = int(mask) - 1 # DEF is 1-based indexing
+        layer = cfg[m.group('layer')][opc_type]['klayout'][mask]
+        xlo = int(m.group('xlo')) / units
+        ylo = int(m.group('ylo')) / units
+        xhi = int(m.group('xhi')) / units
+        yhi = int(m.group('yhi')) / units
+        top.shapes(layer).insert(pya.DBox(xlo, ylo, xhi, yhi))
+      elif re.match('FILLS \d+ ;', line):
+        in_fills = True
+      elif not units:
+        m = re.match('UNITS DISTANCE MICRONS (\d+)', line)
+        if m:
+          units = float(m.group(1))
+
+# Load technology file
+tech = pya.Technology()
+tech.load(tech_file)
+layoutOptions = tech.load_layout_options
+
+# Load def file
+main_layout = pya.Layout()
+print("[INFO] Reporting cells prior to loading DEF ...")
+for i in main_layout.each_cell():
+  print("[INFO] '{0}'".format(i.name))
+
+print("[INFO] Reading DEF ...")
+main_layout.read(in_def, layoutOptions)
+
+#print("[INFO] Reporting cells after loading DEF ...")
+#for i in main_layout.each_cell():
+#  print("[INFO] '{0}'".format(i.name))
+
+# Clear cells
+top_cell_index = main_layout.cell(design_name).cell_index()
+
+# remove orphan cell BUT preserve cell with VIA_
+#  - KLayout is prepending VIA_ when reading DEF that instantiates LEF's via
+print("[INFO] Clearing cells...")
+for i in main_layout.each_cell():
+  if i.cell_index() != top_cell_index:
+    if not i.name.startswith("VIA_"):
+      i.clear()
+
+# Load in the gds to merge
+print("[INFO] Merging GDS/OAS files...")
+for fil in in_files.split():
+  print("\t{0}".format(fil))
+  main_layout.read(fil)
+
+# Copy the top level only to a new layout
+print("[INFO] Copying toplevel cell '{0}'".format(design_name))
+top_only_layout = pya.Layout()
+top_only_layout.dbu = main_layout.dbu
+top = top_only_layout.create_cell(design_name)
+top.copy_tree(main_layout.cell(design_name))
+
+read_fills(top)
+
+print("[INFO] Checking for missing cell from GDS/OAS...")
+missing_cell = False
+regex = None
+if 'GDS_ALLOW_EMPTY' in os.environ:
+    print("[INFO] Found GDS_ALLOW_EMPTY variable.")
+    regex = os.getenv('GDS_ALLOW_EMPTY')
+for i in top_only_layout.each_cell():
+  if i.is_empty():
+    missing_cell = True
+    if regex is not None and re.match(regex, i.name):
+        print("[WARNING] LEF Cell '{0}' ignored. Matches GDS_ALLOW_EMPTY.".format(i.name))
+    else:
+        print("[ERROR] LEF Cell '{0}' has no matching GDS/OAS cell."
+              " Cell will be empty.".format(i.name))
+        errors += 1
+
+if not missing_cell:
+  print("[INFO] All LEF cells have matching GDS/OAS cells")
+
+print("[INFO] Checking for orphan cell in the final layout...")
+orphan_cell = False
+for i in top_only_layout.each_cell():
+  if i.name != design_name and i.parent_cells() == 0:
+    orphan_cell = True
+    print("[ERROR] Found orphan cell '{0}'".format(i.name))
+    errors += 1
+
+if not orphan_cell:
+  print("[INFO] No orphan cells")
+
+
+if seal_file:
+
+  top_cell = top_only_layout.top_cell()
+
+  print("[INFO] Reading seal GDS/OAS file...")
+  print("\t{0}".format(seal_file))
+  top_only_layout.read(seal_file)
+
+  for cell in top_only_layout.top_cells():
+    if cell != top_cell:
+      print("[INFO] Merging '{0}' as child of '{1}'".format(cell.name, top_cell.name))
+      top.insert(pya.CellInstArray(cell.cell_index(), pya.Trans()))
+
+# Write out the GDS
+print("[INFO] Writing out GDS/OAS '{0}'".format(out_file))
+top_only_layout.write(out_file)
+
+sys.exit(errors)
diff --git a/src/hammer-vlsi/hammer_vlsi/vendor/openroad.py b/src/hammer-vlsi/hammer_vlsi/vendor/openroad.py
index 901f573e0..5c487b59e 100644
--- a/src/hammer-vlsi/hammer_vlsi/vendor/openroad.py
+++ b/src/hammer-vlsi/hammer_vlsi/vendor/openroad.py
@@ -17,10 +17,11 @@
 from hammer_utils import add_dicts
 from hammer_vlsi import HasSDCSupport, HammerSynthesisTool, \
                         HammerPlaceAndRouteTool, HammerDRCTool, \
-                        PlacementConstraintType, TimeValue
+                        PlacementConstraintType, TimeValue, \
+                        TCLTool, HammerTool
 import hammer_tech
 
-class OpenROADTool(HasSDCSupport):
+class OpenROADTool(HasSDCSupport, TCLTool, HammerTool):
     """ Mix-in trait with functions useful for OpenROAD-flow tools."""
 
     @property
@@ -45,8 +46,8 @@ def validate_openroad_installation(self) -> None:
         klayout is not installed with OPENROAD as of version 1.1.0. this 
         should be called in steps that actually run tools or touch filepaths
         """
-        if "OPENROAD" not in os.environ:
-            raise Exception("OPENROAD is not defined in environment!")
+        if not shutil.which("openroad"):
+            raise Exception("openroad is not in PATH")
         if not shutil.which("klayout"):
             raise Exception("klayout is not in PATH")
 
@@ -145,9 +146,10 @@ class OpenROADSynthesisTool(HammerSynthesisTool, OpenROADTool):
     #=========================================================================
     # overrides from parent classes
     #=========================================================================
-    @property
-    def post_synth_sdc(self) -> Optional[str]:
-        return None
+    # @property
+    # don't overrride this!
+    # def post_synth_sdc(self) -> Optional[str]:
+    #     return None
 
     #=========================================================================
     # OpenROAD synthesis-specific stuff
diff --git a/src/hammer-vlsi/par/openroad/__init__.py b/src/hammer-vlsi/par/openroad/__init__.py
old mode 100644
new mode 100755
index 1af1f9498..b868bc8a6
--- a/src/hammer-vlsi/par/openroad/__init__.py
+++ b/src/hammer-vlsi/par/openroad/__init__.py
@@ -5,15 +5,26 @@
 #
 # See LICENSE for licence details.
 
+# NOTE: any hard-coded values are from OpenROAD example flow
+
 import glob
 import os
 from textwrap import dedent as dd
-from typing import List, Optional, Dict, Any
+from typing import List, Optional, Dict, Any, Tuple, Callable
+from decimal import Decimal
+from pathlib import Path
 
 from hammer_logging import HammerVLSILogging
-from hammer_utils import deepdict
-from hammer_vlsi import HammerToolStep
-from hammer_vlsi.vendor import OpenROADPlaceAndRouteTool
+from hammer_utils import deepdict, optional_map
+from hammer_vlsi import HammerTool, HammerPlaceAndRouteTool, HammerToolStep, HammerToolHookAction, MMMCCornerType, PlacementConstraintType, TCLTool
+from hammer_vlsi.units import TimeValue, CapacitanceValue
+from hammer_vlsi.constraints import MMMCCorner, MMMCCornerType
+from hammer_vlsi.vendor import OpenROADTool, OpenROADPlaceAndRouteTool
+
+import hammer_tech
+from hammer_tech import RoutingDirection
+import specialcells
+from specialcells import CellType, SpecialCell
 
 class OpenROADPlaceAndRoute(OpenROADPlaceAndRouteTool):
 
@@ -25,41 +36,155 @@ def steps(self) -> List[HammerToolStep]:
         return self.make_steps_from_methods([
             self.init_design,
             self.floorplan_design,
-            self.place_bumps, # nop
-            self.place_tap_cells, # nop
-            self.power_straps, # nop
-            self.place_pins, # nop
+            self.place_tap_cells, 
+            self.power_straps, 
+            self.global_placement,
+            self.place_pins, 
             self.place_opt_design,
             self.clock_tree,
-            self.add_fillers, # nop
-            self.route_design,
-            self.opt_design, # nop
-            self.write_regs, # nop
+            self.add_fillers, 
+            self.global_route,
+            self.detailed_route,
+            self.extraction,
             self.write_design,
         ])
 
+    def get_tool_hooks(self) -> List[HammerToolHookAction]:
+        return [self.make_persistent_hook(openroad_global_settings)]
+
+    @property
+    def _step_transitions(self) -> List[Tuple[str, str]]:
+        """
+        Private helper property to keep track of which steps we ran so that we
+        can create symlinks.
+        This is a list of (pre, post) steps
+        """
+        return self.attr_getter("__step_transitions", [])
+
+    @_step_transitions.setter
+    def _step_transitions(self, value: List[Tuple[str, str]]) -> None:
+        self.attr_setter("__step_transitions", value)
+
     def do_pre_steps(self, first_step: HammerToolStep) -> bool:
         assert super().do_pre_steps(first_step)
-        self.cmds = []
+        # self.cmds = []
+        # Restore from the last checkpoint if we're not starting over.
+        if first_step != self.first_step:
+            self.append(self.read_liberty())
+            self.append("read_db pre_{step}".format(step=first_step.name))
+            self.read_sdc()
+        return True
+
+    def do_between_steps(self, prev: HammerToolStep, next: HammerToolStep) -> bool:
+        assert super().do_between_steps(prev, next)
+        # Write a checkpoint to disk.
+        self.append("write_db pre_{step}".format(step=next.name))
+        # Symlink the database to latest for open_chip script later.
+        self.append("ln -sfn pre_{step} latest".format(step=next.name))
+        self._step_transitions = self._step_transitions + [(prev.name, next.name)]
         return True
 
     def do_post_steps(self) -> bool:
         assert super().do_post_steps()
         return self.run_openroad()
 
+    @property
+    def all_regs_path(self) -> str:
+        return os.path.join(self.run_dir, "find_regs_paths.json")
+
+    @property
+    def all_cells_path(self) -> str:
+        return os.path.join(self.run_dir, "find_regs_cells.json")
+
+    @property
+    def output_sdf_path(self) -> str:
+        return os.path.join(self.run_dir, "{top}.par.sdf".format(top=self.top_module))
+
+    @property
+    def output_spef_paths(self) -> str:
+        return os.path.join(self.run_dir, "{top}.par.spef".format(top=self.top_module))
+
+    @property
+    def route_guide_path(self) -> str:
+        return os.path.join(self.run_dir, "{top}.route_guide".format(top=self.top_module))
+
+    @property
+    def klayout_techfile_path(self) -> str:
+        klayout_techfiles = self.technology.read_libs([
+            hammer_tech.filters.klayout_techfile_filter
+        ], hammer_tech.HammerTechnologyUtils.to_plain_item)
+        return klayout_techfiles[0]
+
     @property
     def env_vars(self) -> Dict[str, str]:
-        new_dict = deepdict(super().env_vars)
-        new_dict.update({})  # TODO: stuffs
-        return new_dict
+        v = dict(super().env_vars)
+        v["OPENROAD_BIN"] = self.get_setting("par.openroad.openroad_bin")
+        return v
+
+    @property
+    def output_def_filename(self) -> str:
+        return os.path.join(self.run_dir, "{top}.def".format(top=self.top_module))
+
+    @property
+    def output_gds_filename(self) -> str:
+        return os.path.join(self.run_dir, "{top}.gds".format(top=self.top_module))
+
+    @property
+    def output_netlist_filename(self) -> str:
+        return os.path.join(self.run_dir, "{top}.lvs.v".format(top=self.top_module))
+
+    @property
+    def output_sim_netlist_filename(self) -> str:
+        return os.path.join(self.run_dir, "{top}.sim.v".format(top=self.top_module))
+    
+    @property
+    def generated_scripts_dir(self) -> str:
+        return os.path.join(self.run_dir, "generated-scripts")
+    
+    @property
+    def open_chip_script(self) -> str:
+        return os.path.join(self.generated_scripts_dir, "open_chip")
+
+    @property
+    def open_chip_tcl(self) -> str:
+        return self.open_chip_script + ".tcl"
+
+    def tech_lib_filter(self) -> List[Callable[[hammer_tech.Library], bool]]:
+        """ Filter only libraries from tech plugin """
+        return [self.filter_for_tech_libs]
+
+    def filter_for_tech_libs(self, lib: hammer_tech.Library) -> bool:
+        return lib in self.technology.tech_defined_libraries
+
+    def extra_lib_filter(self) -> List[Callable[[hammer_tech.Library], bool]]:
+        """ Filter only libraries from vlsi.inputs.extra_libraries """
+        return [self.filter_for_extra_libs]
+
+    def filter_for_extra_libs(self, lib: hammer_tech.Library) -> bool:
+        return lib in list(map(lambda el: el.store_into_library(), self.technology.get_extra_libraries()))
+
+    @property
+    def fill_cells(self) -> str:
+        stdfillers = self.technology.get_special_cell_by_type(CellType.StdFiller)
+        return ' '.join(list(map(lambda c: str(c), stdfillers[0].name)))
+
+    def block_append(self,commands,file=None) -> bool:
+        if file is None:
+            for line in commands.split('\n'):
+                self.append(line.strip())
+        else:
+            for line in commands.split('\n'):
+                file.write(line.strip())
+                file.write('\n')
+        return True
 
     def fill_outputs(self) -> bool:
         # TODO: no support for ILM
         self.output_ilms = []
 
-        self.output_gds = self.output_gds_path()
-        self.output_netlist = self.output_v_path()
-        self.output_sim_netlist = self.output_v_path()
+        self.output_gds = self.output_gds_filename
+        self.output_netlist = self.output_netlist_filename
+        self.output_sim_netlist = self.output_sim_netlist_filename
 
         # TODO: support outputting the following
         self.hcells_list = []
@@ -69,7 +194,6 @@ def fill_outputs(self) -> bool:
 
         return True
 
-
     def export_config_outputs(self) -> Dict[str, Any]:
         outputs = dict(super().export_config_outputs())
         outputs["par.outputs.seq_cells"] = self.output_seq_cells
@@ -80,152 +204,1013 @@ def export_config_outputs(self) -> Dict[str, Any]:
     def tool_config_prefix(self) -> str:
         return "par.openroad"
 
+    def gui(self) -> str:
+        cmds=[]
+        cmds.append(self.read_liberty())
+        cmds.append("read_db latest")
+        return '\n'.join(cmds)
+
     #=========================================================================
     # useful subroutines
     #=========================================================================
 
-    def output_gds_path(self) -> str:
-        return os.path.join(self.run_dir, 
-          "results/{}/{}/6_final.gds".format(
-            self.get_setting("vlsi.core.technology"),
-            self.top_module))
+    def run_openroad(self) -> bool:
+        # Quit OpenROAD.
+        self.append("exit")
 
-    def output_v_path(self) -> str:
-        return os.path.join(self.run_dir, 
-          "results/{}/{}/5_route.v".format(
-            self.get_setting("vlsi.core.technology"),
-            self.top_module))
+        # Create par script.
+        par_tcl_filename = os.path.join(self.run_dir, "par.tcl")
+        with open(par_tcl_filename, "w") as f:
+            f.write("\n".join(self.output))
 
-    def run_openroad(self) -> bool:
-        run_script = os.path.join(self.run_dir, "par.sh")
-
-        self.validate_openroad_installation()
-        self.setup_openroad_rundir()
-
-        with open(run_script, "w") as f:
-            f.write(dd("""\
-              #!/bin/bash
-              cd "{rundir}"
-              mkdir -p logs/{tech}/{name}
-              mkdir -p objects/{tech}/{name}
-              mkdir -p reports/{tech}/{name}
-              mkdir -p results/{tech}/{name}
-            """.format(
-              rundir=self.run_dir,
-              tech=self.get_setting("vlsi.core.technology"),
-              name=self.top_module, 
-            )))
-            f.write("\n".join(self.cmds))
-        os.chmod(run_script, 0o755)
+        # Create open_chip script pointing to latest (symlinked to post_<last ran step>).
+        with open(self.open_chip_tcl, "w") as f:
+            f.write(self.gui())
+
+        with open(self.open_chip_script, "w") as f:
+            f.write("""#!/bin/bash
+        cd {run_dir}
+        source enter
+        $OPENROAD_BIN -no_init -gui {open_chip_tcl}
+                """.format(run_dir=self.run_dir, open_chip_tcl=self.open_chip_tcl))
+        os.chmod(self.open_chip_script, 0o755)
+
+        # Build args.
+        args = [
+            self.get_setting("par.openroad.openroad_bin"),
+            "-no_init",             # do not read .openroad init file
+            "-exit",                # exit after reading par_tcl_filename
+            par_tcl_filename
+        ]
 
         if bool(self.get_setting("par.openroad.generate_only")):
-            self.logger.info("Generate-only mode: command-line is " + \
-                             " ".join(args))
+            self.logger.info("Generate-only mode: command-line is " + " ".join(args))
         else:
-            # Temporarily disable colors/tag to make run output more readable
+            # Temporarily disable colours/tag to make run output more readable.
             # TODO: think of a more elegant way to do this?
             HammerVLSILogging.enable_colour = False
             HammerVLSILogging.enable_tag = False
-            self.run_executable([run_script]) # TODO: check for errors
+            self.run_executable(args, cwd=self.run_dir)  # TODO: check for errors and deal with them
             HammerVLSILogging.enable_colour = True
             HammerVLSILogging.enable_tag = True
 
         return True
+    
+    def get_timing_libs(self, corner: Optional[MMMCCorner] = None) -> str:
+        """
+        Helper function to get the list of ASCII timing .lib files in space separated format.
+        Note that Cadence tools support ECSM, so we can use the ECSM-based filter.
+
+        :param corner: Optional corner to consider. If supplied, this will use filter_for_mmmc to select libraries that
+        match a given corner (voltage/temperature).
+        :return: List of lib files separated by spaces
+        """
+        pre_filters = optional_map(corner, lambda c: [self.filter_for_mmmc(voltage=c.voltage,temp=c.temp)])  # type: Optional[List[Callable[[hammer_tech.Library],bool]]]
+        lib_args = self.technology.read_libs([hammer_tech.filters.timing_lib_with_ecsm_filter], hammer_tech.HammerTechnologyUtils.to_plain_item, extra_pre_filters=pre_filters)
+        return " ".join(lib_args)
+
+    def read_lef(self) -> bool:
+        # OpenROAD names the LEF libraries by filename:
+        #   foo.tlef and foo.lef evaluate to the same library "foo"
+        #   solution: copy foo.lef to foo1.lef
+        lef_files = self.technology.read_libs([
+            hammer_tech.filters.lef_filter
+        ], hammer_tech.HammerTechnologyUtils.to_plain_item)
+        if self.hierarchical_mode.is_nonleaf_hierarchical():
+            ilm_lefs = list(map(lambda ilm: ilm.lef, self.get_input_ilms()))
+            lef_files.extend(ilm_lefs)
+        lef_file_libnames=[]
+        unique_id=0
+        for lef_file in lef_files:
+            lef_file_name=lef_file.split('/')[-1]
+            lef_file_libname=''.join(lef_file_name.split('.')[:-1])
+            lef_file_ext=lef_file_name.split('.')[-1]
+            if lef_file_libname in lef_file_libnames:
+                lef_file_libname=f"{lef_file_libname}_{unique_id}.{lef_file_ext}"
+                new_lef_file=f"{self.run_dir}/{lef_file_libname}"
+                with open(lef_file,'r') as f_old:
+                    with open(new_lef_file,'w') as f_new:
+                        for line in f_old: 
+                            f_new.write(line)
+                lef_file=new_lef_file
+                unique_id+=1
+            lef_file_libnames.append(lef_file_libname)
+            self.append(f"read_lef {lef_file}")
+        self.append("")
+        return True
+
+    def read_liberty(self) -> str:
+        cmds=[]
+        corners = self.get_mmmc_corners()  # type: List[MMMCCorner]
+        if corners:
+            corner_names = []
+            for corner in corners:
+                # Setting up views for all defined corner types: setup, hold, extra
+                if corner.type is MMMCCornerType.Setup:
+                    corner_name="setup"
+                elif corner.type is MMMCCornerType.Hold:
+                    corner_name="hold"
+                elif corner.type is MMMCCornerType.Extra:
+                    corner_name="extra"
+                else:
+                    raise ValueError("Unsupported MMMCCornerType")
+                corner_names.append(corner_name)
+            
+            cmds.append(f"define_corners {' '.join(corner_names)}")
+            for corner,corner_name in zip(corners,corner_names):
+                lib_files=self.get_timing_libs(corner)
+                for lib_file in lib_files.split():
+                    cmds.append(f"read_liberty -corner {corner_name} {lib_file}")
+        cmds.append("")
+        return '\n'.join(cmds)
+
+    def scale_units_1000x_down(self,prefix) -> str:
+        # convert SI prefix down by 1000x
+        if prefix == 'a':
+            return 'f'
+        if prefix == 'f':
+            return 'p'
+        if prefix == 'p':
+            return 'n'
+        if prefix == 'n':
+            return 'u'
+        if prefix == 'u':
+            return 'm'
+        if prefix == 'm':
+            return ''
+        return ''
+        
+    def read_sdc(self) -> bool:
+        # overwrite SDC file to exclude group_path command
+        # change units in SDC file (1000.0fF and 1000.0ps cause errors)
+
+        sdc_files = self.generate_sdc_files()
+        for sdc_file in sdc_files:
+            self.append(f"read_sdc -echo {sdc_file}")
+
+        return True
 
     #========================================================================
     # par main steps
     #========================================================================
     def init_design(self) -> bool:
-        # TODO: currently hardlinking the syn outputs, otherwise the 
-        # OpenROAD-flow's default makefile will rebuild syn in this directory
-        syn_dirs = {
-          "results": self.syn_results_path(),
-          "objects": self.syn_objects_path()
-        }
-        for syn_dir in ["results", "objects"]:
-          for src in glob.glob(os.path.join(syn_dirs[syn_dir], "*")):
-              dst = "{syn_dir}/{tech}/{name}/{base}".format(
-                  syn_dir=syn_dir,
-                  tech=self.get_setting("vlsi.core.technology"),
-                  name=self.top_module,
-                  base=os.path.basename(src))
-              self.cmds += [
-                  "rm -f {}".format(dst), 
-                  "ln {} {}".format(src, dst)
-              ]
+        # set up useful variables
+        clock_port = self.get_clock_ports()[0]
+        self.clock_port_name = clock_port.name
+        tech_base_dir=self.get_setting('vlsi.core.technology_path')[-1]
+        tech = self.get_setting('vlsi.core.technology')
+
+        # start routine
+        self.read_lef()
+        self.append(self.read_liberty())
+        
+        # read_verilog
+        # We are switching working directories and we still need to find paths.
+        abspath_input_files = list(map(lambda name: os.path.join(os.getcwd(), name), self.input_files))
+        for verilog_file in abspath_input_files:
+            self.append(f"read_verilog {verilog_file}")
+        self.append(f"link_design {self.top_module}\n")
+        self.read_sdc()
         return True
 
     def floorplan_design(self) -> bool:
-        # TODO: currently using OpenROAD's default floorplan script
-        self.cmds += [
-          "make DESIGN_CONFIG={conf} -f {make} floorplan".format(
-            conf=self.design_config_path(), 
-            make=self.openroad_flow_makefile_path()
-        )]
-        return True
 
-    def place_bumps(self) -> bool:
-        # TODO: currently using OpenROAD's default floorplan script
+        floorplan_tcl = os.path.join(self.run_dir, "floorplan.tcl")
+        with open(floorplan_tcl, "w") as f:
+            f.write("\n".join(self.create_floorplan_tcl()))
+
+        self.block_append(f"""
+        ################################################################
+        # Floorplan Design
+
+        # Init floorplan/Place Macros
+        source -echo -verbose {floorplan_tcl}
+
+        # create routing tracks
+        {self.generate_make_tracks()}
+
+        # remove buffers inserted by synthesis 
+        remove_buffers
+
+        # IO Placement (random)
+        {self.place_pins_tcl(random=True)}
+        """)
+        
         return True
 
     def place_tap_cells(self) -> bool:
-        # TODO: currently using OpenROAD's default floorplan script
+        tap_cells = self.technology.get_special_cell_by_type(CellType.TapCell)
+        endcap_cells = self.technology.get_special_cell_by_type(CellType.EndCap)
+        if len(tap_cells) == 0:
+            self.logger.warning("Tap cells are improperly defined in the tech plugin and will not be added. This step should be overridden with a user hook.")
+            return True
+        tap_cell = tap_cells[0].name[0]
+        endcap_cell=endcap_cells[0].name[0]
+        try:
+            interval = self.get_setting("vlsi.technology.tap_cell_interval")
+            offset = self.get_setting("vlsi.technology.tap_cell_offset")
+            self.block_append(f"""
+            ################################################################
+            # Tapcell insertion
+            tapcell -tapcell_master {tap_cell} -endcap_master {endcap_cell} -distance {interval} -halo_width_x {offset} -halo_width_y {offset}
+            """)
+        except KeyError:
+            pass
+        finally:
+            self.logger.warning(
+                "You have not overridden place_tap_cells. By default this step adds a simple set of tapcells or does nothing; you will have trouble with power strap creation later.")
         return True
 
+    def generate_pdn_config(self, pdn_config_path) -> bool:
+        pwr_nets=self.get_all_power_nets()
+        gnd_nets=self.get_all_ground_nets()
+        primary_pwr_net=pwr_nets[0].name
+        primary_gnd_net=gnd_nets[0].name
+        all_metal_layer_names = [layer.name for layer in self.get_stackup().metals]
+
+        strap_layers = self.get_setting("par.generate_power_straps_options.by_tracks.strap_layers").copy()
+        std_cell_rail_layer = str(self.get_setting("technology.core.std_cell_rail_layer"))
+        strap_layers.insert(0,std_cell_rail_layer)
+        
+        metal_pairs=""
+        for i in range(0,len(strap_layers)-1):
+            metal_pairs+=f"{{{strap_layers[i]} {strap_layers[i+1]}}} "
+        
+        global_connections_pwr=[]
+        for pwr_net in pwr_nets:
+            if pwr_net.tie is not None:
+                global_connections_pwr.append(f"\n{{inst_name .* pin_name {pwr_net.name}}}")
+        
+        global_connections_gnd=[]
+        for gnd_net in gnd_nets:
+            if gnd_net.tie is not None:
+                global_connections_gnd.append(f"\n{{inst_name .* pin_name {gnd_net.name}}}")
+
+        pdn_cfg=f"""
+        # Floorplan information - core boundary coordinates, std. cell row height,
+        # minimum track pitch as defined in LEF
+
+        # POWER or GROUND #Std. cell rails starting with power or ground rails at the bottom of the core area
+        set ::rails_start_with "POWER" ;
+
+        # POWER or GROUND #Upper metal stripes starting with power or ground rails at the left/bottom of the core area
+        set ::stripes_start_with "POWER" ;
+
+        # Power nets
+        set ::power_nets  "{primary_pwr_net}"
+        set ::ground_nets "{primary_gnd_net}"
+
+        set pdngen::global_connections {{
+            {primary_pwr_net} {{
+                {' '.join(global_connections_pwr)}
+        }}
+            {primary_gnd_net} {{
+                {' '.join(global_connections_gnd)}
+            }}
+        }}
+        ##===> Power grid strategy
+        # Ensure pitches and offsets will make the stripes fall on track
+
+        pdngen::specify_grid stdcell {{
+            name grid
+            rails {{
+                met1 {{width 0.48 offset 0}}
+            }}
+            straps {{
+                {' '.join(self.create_power_straps_tcl())}
+            }}
+            connect {{{metal_pairs}}}
+        }}
+
+        pdngen::specify_grid macro {{
+            orient {{R0 R180 MX MY}}
+            power_pins  "{' '.join(self.get_setting("technology.core.std_cell_supplies.power"))}"
+            ground_pins "{' '.join(self.get_setting("technology.core.std_cell_supplies.ground"))}"
+            blockages "{" ".join(all_metal_layer_names[:-1])}"
+            # TODO: where does this met4_PIN_ver come from????
+            connect {{{{met4_PIN_ver met5}}}}
+            # or: connect {{{{met4_PIN_hor met5}}}}
+        }}
+        """
+
+        with open(pdn_config_path, "w") as f:
+            f.write(pdn_cfg)
+        
+        return True
+
+    def write_power_straps_tcl(self, power_straps_tcl_path) -> bool:
+        pwr_nets=self.get_all_power_nets()
+        gnd_nets=self.get_all_ground_nets()
+        primary_pwr_net=pwr_nets[0].name
+        primary_gnd_net=gnd_nets[0].name
+        all_metal_layer_names = [layer.name for layer in self.get_stackup().metals]
+
+        strap_layers = self.get_setting("par.generate_power_straps_options.by_tracks.strap_layers").copy()
+        std_cell_rail_layer = str(self.get_setting("technology.core.std_cell_rail_layer"))
+        strap_layers.insert(0,std_cell_rail_layer)
+        
+        add_pdn_connect_tcl=""
+        for i in range(0,len(strap_layers)-1):
+            add_pdn_connect_tcl+=f"add_pdn_connect -grid grid -layers {{{strap_layers[i]} {strap_layers[i+1]}}}\n"
+        
+        global_connections_pwr_tcl=f"add_global_connection -net {{{primary_pwr_net}}} -inst_pattern {{.*}} -pin_pattern {{^{primary_pwr_net}$}} -power"
+        global_connections_gnd_tcl=f"add_global_connection -net {{{primary_gnd_net}}} -inst_pattern {{.*}} -pin_pattern {{^{primary_gnd_net}$}} -ground"
+        for pwr_net in pwr_nets:
+            if pwr_net.tie is not None:
+                global_connections_pwr_tcl += f"\n add_global_connection -net {{{primary_pwr_net}}} -inst_pattern {{.*}} -pin_pattern {{{pwr_net.name}}}"
+        for gnd_net in gnd_nets:
+            if gnd_net.tie is not None:
+                global_connections_gnd_tcl += f"\n add_global_connection -net {{{primary_gnd_net}}} -inst_pattern {{.*}} -pin_pattern {{{gnd_net.name}}}"
+
+        blockage_spacing = self.get_setting("par.blockage_spacing")
+        blockage_spacing_halo = [blockage_spacing for i in range(4)]
+
+        with open(power_straps_tcl_path,'w') as power_straps_tcl_file:
+            self.block_append(
+            f"""
+            ####################################
+            # global connections
+            ####################################
+            {global_connections_pwr_tcl}
+            {global_connections_gnd_tcl}
+
+            # add_global_connection -net {{primary_pwr_net}} -inst_pattern {{.*}} -pin_pattern {{^VDDPE$}}
+            # add_global_connection -net {{primary_pwr_net}} -inst_pattern {{.*}} -pin_pattern {{^VDDCE$}}
+            # add_global_connection -net {{primary_gnd_net}} -inst_pattern {{.*}} -pin_pattern {{^VSSE$}}
+
+            ####################################
+            # voltage domains
+            ####################################
+            # AO is hard-coded in cpf generation too
+            set_voltage_domain -name {{AO}} -power {{{primary_pwr_net}}} -ground {{{primary_gnd_net}}}
+
+            ####################################
+            # standard cell grid
+            ####################################
+            define_pdn_grid -name {{grid}} -voltage_domains {{AO}}
+            {' '.join(self.create_power_straps_tcl())}
+            {add_pdn_connect_tcl}
+
+            ####################################
+            # macro grids
+            ####################################
+            ####################################
+            # grid for: CORE_macro_grid_1
+            ####################################
+            define_pdn_grid -name {{CORE_macro_grid_1}} -voltage_domains {{CORE}} -macro -orient {{R0 R180 MX MY}} -halo {{ {blockage_spacing_halo} }} -grid_over_boundary
+            add_pdn_connect -grid {{CORE_macro_grid_1}} -layers {{met4 met5}}
+            
+            ####################################
+            # grid for: CORE_macro_grid_2
+            ####################################
+            #define_pdn_grid -name {{CORE_macro_grid_2}} -voltage_domains {{CORE}} -macro -orient {{R90 R270 MXR90 MYR90}} -halo {{ {blockage_spacing_halo} }} -grid_over_boundary
+            #add_pdn_connect -grid {{CORE_macro_grid_2}} -layers {{met4 met5}}
+            """,
+            file=power_straps_tcl_file)
+        
+        return True
+    
     def power_straps(self) -> bool:
-        # TODO: currently using OpenROAD's default floorplan script
+        """Place the power straps for the design."""
+        power_straps_tcl_path = os.path.join(self.run_dir, "power_straps.pdn")
+        self.generate_pdn_config(power_straps_tcl_path)
+        self.block_append(f"""
+        ################################################################
+        # Power distribution network insertion
+        pdngen -verbose {power_straps_tcl_path}
+        """)
+        return True        
+
+    def global_placement(self) -> bool:
+        metals=self.get_stackup().metals[1:]
+        spacing = self.get_setting("par.blockage_spacing")
+        idx_clock_bottom_metal=min(2,len(metals)-1)
+        
+        self.block_append(f"""
+        ################################################################
+        # Global placement
+        set_global_routing_layer_adjustment {metals[0].name}-{metals[-1].name} 0
+        set_routing_layers -signal {metals[0].name}-{metals[-1].name} -clock {metals[idx_clock_bottom_metal].name}-{metals[-1].name}
+        set_macro_extension {int(spacing)}
+
+        # -density default is 0.7, overflow default is 0.3
+        # set overflow higher (ex. 0.8) to make faster
+        global_placement -routability_driven -pad_left 4 -pad_right 4 -overflow 0.2
+
+        estimate_parasitics -placement
+        """)
+
         return True
 
+    def place_pins_tcl(self,random=False) -> str:
+        random_arg=""
+        if random: random_arg="-random"
+
+        stackup = self.get_stackup()
+        all_metal_layer_names = [layer.name for layer in self.get_stackup().metals]
+        pin_assignments = self.get_pin_assignments()
+        hor_layers=[]
+        ver_layers=[]
+        for pin in pin_assignments:
+            if pin.layers is not None and len(pin.layers) > 0:
+                for pin_layer_name in pin.layers:
+                    layer = self.get_stackup().get_metal(pin_layer_name)
+                    if layer.direction==RoutingDirection.Horizontal:
+                        hor_layers.append(pin_layer_name)
+                    if layer.direction==RoutingDirection.Vertical:
+                        ver_layers.append(pin_layer_name)
+        
+        # both hor_layers and ver_layers arguments are required
+        # if missing, auto-choose one or both
+        if not (hor_layers and ver_layers):
+            self.logger.warning("Both horizontal and vertical pin layers should be specified. Hammer will auto-specify one or both.")
+            # choose first pin layer to be middle of stackup
+            #   or use pin layer in either hor_layers or ver_layers
+            pin_layer_names=["",""]
+            pin_layer_names[0]=all_metal_layer_names[int(len(all_metal_layer_names)/2)]
+            if (hor_layers): pin_layer_names[0]=hor_layers[0]
+            if (ver_layers): pin_layer_names[0]=ver_layers[0]
+            pin_layer_idx_1=all_metal_layer_names.index(pin_layer_names[0])
+            if (pin_layer_idx_1 < len(all_metal_layer_names)-1): 
+                pin_layer_names[1]=all_metal_layer_names[pin_layer_idx_1+1]
+            elif (pin_layer_idx_1 > 0): 
+                pin_layer_names[1]=all_metal_layer_names[pin_layer_idx_1-1]
+            else: # edge-case
+                pin_layer_names[1]=all_metal_layer_names[pin_layer_idx_1]
+            for pin_layer_name in pin_layer_names:
+                layer = self.get_stackup().get_metal(pin_layer_name)
+                if (layer.direction==RoutingDirection.Horizontal) and (pin_layer_name not in hor_layers):
+                    hor_layers.append(pin_layer_name)
+                if (layer.direction==RoutingDirection.Vertical)   and (pin_layer_name not in ver_layers):
+                    ver_layers.append(pin_layer_name)
+        # determine commands for side specified in pin assignments
+        #   can only be done in openroad by "excluding" the entire length of the other 3 sides from pin placement
+        side=""
+        for pin in pin_assignments:
+            if pin.side == "bottom":
+                side="-exclude top:* -exclude right:* -exclude left:*"
+            elif pin.side == "top":
+                side="-exclude bottom:* -exclude right:* -exclude left:*"
+            elif pin.side == "left":
+                side="-exclude top:* -exclude right:* -exclude bottom:*"
+            elif pin.side == "right":
+                side="-exclude top:* -exclude bottom:* -exclude left:*"
+
+        return f"place_pins {random_arg} -hor_layers {{{' '.join(hor_layers)}}} -ver_layers {{{' '.join(ver_layers)}}} {side}"
+
     def place_pins(self) -> bool:
-        # TODO: currently using OpenROAD's default floorplan script
+        self.block_append(f"""
+        ################################################################
+        # IO Placement
+        {self.place_pins_tcl()}
+        """)
         return True
 
     def place_opt_design(self) -> bool:
-        # TODO: currently using OpenROAD's default place script
-        self.cmds += [
-          "make DESIGN_CONFIG={conf} -f {make} place".format(
-            conf=self.design_config_path(), 
-            make=self.openroad_flow_makefile_path()
-        )]
+        tie_hi_cells = self.technology.get_special_cell_by_type(CellType.TieHiCell)
+        tie_lo_cells = self.technology.get_special_cell_by_type(CellType.TieLoCell)
+        tie_hilo_cells = self.technology.get_special_cell_by_type(CellType.TieHiLoCell)
+
+        if len(tie_hi_cells) is None or len (tie_lo_cells) is None or tie_hi_cells[0].input_ports is None or tie_lo_cells[0].input_ports is None:
+            self.logger.warning("Hi and Lo tiecells and their input ports are unspecified or improperly specified and will not be added.")
+        else:   
+            tie_hi_cell = tie_hi_cells[0].name[0]
+            tie_hi_port = tie_hi_cells[0].input_ports[0]
+            tie_lo_cell = tie_lo_cells[0].name[0]
+            tie_lo_port = tie_lo_cells[0].input_ports[0]
+
+        self.block_append(f"""
+        set_dont_use {{{' '.join(self.get_dont_use_list())}}}
+        estimate_parasitics -placement
+        repair_design -slew_margin 0 -cap_margin 0
+
+        repair_tie_fanout -separation 0 "{tie_hi_cell}/{tie_hi_port}"
+        repair_tie_fanout -separation 0 "{tie_lo_cell}/{tie_lo_port}"
+
+        # default detail_place_pad value in OpenROAD = 2
+        set_placement_padding -global -left 4 -right 4
+        detailed_placement
+
+        # post resize timing report (ideal clocks)
+        report_worst_slack -min -digits 3
+        report_worst_slack -max -digits 3
+        report_tns -digits 3
+
+        # Check slew repair
+        report_check_types -max_slew -max_capacitance -max_fanout -violators
+
+        # check_placement -verbose
+        """)
         return True
 
     def clock_tree(self) -> bool:
-        # TODO: currently using OpenROAD's default cts script
-        self.cmds += [
-          "make DESIGN_CONFIG={conf} -f {make} cts".format(
-            conf=self.design_config_path(), 
-            make=self.openroad_flow_makefile_path()
-        )]
+        clock_port = self.get_clock_ports()[0]
+        self.clock_port_name = clock_port.name
+
+        cts_buffer_cells = self.technology.get_special_cell_by_type(CellType.CTSBuffer)
+
+        cts_args=""
+        if cts_buffer_cells is None:
+            self.logger.warning("CTS buffer cells are unspecified.")
+        else:
+            cts_buffer_cell = cts_buffer_cells[0].name[0]
+            cts_args=f"-root_buf {cts_buffer_cell} -buf_list {cts_buffer_cell}"
+
+        self.block_append(f"""
+        check_placement -verbose
+
+        ################################################################
+        # Clock Tree Synthesis
+        set_placement_padding -global -left 2 -right 2
+
+        repair_clock_inverters
+        clock_tree_synthesis {cts_args} -sink_clustering_enable
+        
+        set_propagated_clock [all_clocks]
+
+        # CTS leaves a long wire from the pad to the clock tree root.
+        repair_clock_nets
+        
+        # place clock buffers
+        detailed_placement
+
+        ###########################
+        # Setup/hold timing repair
+
+        set_placement_padding -global -left 0 -right 0
+
+        set_propagated_clock [all_clocks]
+
+        # -placement|-global_routing
+        estimate_parasitics -placement
+
+        repair_timing -setup
+
+        repair_timing -hold
+
+        detailed_placement
+
+        # Post timing repair.
+        report_worst_slack -min -digits 3
+        report_worst_slack -max -digits 3
+        report_tns -digits 3
+
+
+        """)
         return True
 
     def add_fillers(self) -> bool:
-        # TODO: currently no analagous OpenROAD default script
+        """add decap and filler cells"""
+        self.block_append(f"""
+        check_placement -verbose
+        
+        ################################################################
+        # Detailed +  Filler Placement (final)
+        set_placement_padding -global -left 0 -right 0
+        detailed_placement
+        improve_placement
+        check_placement -verbose
+        # optimize_mirroring - tries to reduce wirelength
+        # Capture utilization before fillers make it 100%
+        utl::metric "utilization" [format %.1f [expr [rsz::utilization] * 100]]
+        utl::metric "design_area" [sta::format_area [rsz::design_area] 0]
+
+        filler_placement {{ {self.fill_cells} }}
+        # detailed_placement
+        # check_placement -verbose
+        """)
         return True
 
-    def route_design(self) -> bool:
-        # TODO: currently using OpenROAD's default route script
-        self.cmds += [
-          "make DESIGN_CONFIG={conf} -f {make} route".format(
-            conf=self.design_config_path(), 
-            make=self.openroad_flow_makefile_path()
-        )]
+    def global_route(self) -> bool:
+        metals=self.get_stackup().metals[1:]
+
+        self.block_append(f"""
+        detailed_placement
+        check_placement -verbose
+
+        ################################################################
+        # Global routing
+        set_global_routing_layer_adjustment {metals[0].name}-{metals[-1].name} 0.3
+        set_routing_layers -signal {metals[0].name}-{metals[-1].name} -clock met3-met5
+        set_macro_extension {int(self.get_setting("par.blockage_spacing"))}
+
+        # pin_access - this command caused openroad to crash
+        # -allow_congestion and -allow_overflow added for now
+
+        global_route -guide_file {self.route_guide_path} -allow_congestion -congestion_iterations 200 -verbose
+
+        set_propagated_clock [all_clocks]
+        estimate_parasitics -global_routing
+
+        check_antennas -report_file {self.run_dir}/antenna.log -report_violating_nets
+        """)
         return True
 
-    def opt_design(self) -> bool:
-        # TODO: currently no analagous OpenROAD default script
+    def detailed_route(self) -> bool:
+        metals=self.get_stackup().metals[1:]
+        
+        self.block_append(f"""
+        ################################################################
+        # Detailed routing
+
+        # set_thread_count [exec getconf _NPROCESSORS_ONLN]
+
+        write_verilog -include_pwr_gnd {self.output_netlist_filename}
+
+        # TODO: many other arguments available
+        detailed_route -guide {self.route_guide_path} \\
+            -bottom_routing_layer met1 \\
+            -top_routing_layer met5 \\
+            -output_guide {self.run_dir}/{self.top_module}_output_guide.mod \\
+            -output_drc {self.run_dir}/{self.top_module}_route_drc.rpt \\
+            -output_maze {self.run_dir}/{self.top_module}_maze.log \\
+            -verbose 1 \\
+            -droute_end_iter 3
+
+        #utl::metric "DRT::drv" [detailed_route_num_drvs]
+        #write_def {self.run_dir}/{self.top_module}_route.def
+        """)
+        return True
+
+    def extraction(self) -> bool:
+        sed_expr=r"{s/\\//g}"  # use sed find+replace to remove '\' character
+        self.block_append(f"""
+        ################################################################
+        # Extraction
+        define_process_corner -ext_model_index 0 X
+
+        extract_parasitics -ext_model_file {self.get_setting("par.inputs.openrcx_techfile")}
+
+        # touch the file in case write_spef fails
+        exec touch {self.output_spef_paths}
+        write_spef {self.output_spef_paths}
+        # remove backslashes in instances so that read_spef recognizes the instances
+        exec sed -i {sed_expr} {self.output_spef_paths}
+
+        read_spef {self.output_spef_paths}
+        """)
+        # alternative: use global routing based parasitics inlieu of rc extraction
+        #   >> estimate_parasitics -global_routing
+        return True
+
+    def write_netlist(self) -> bool:
+        self.append(f"write_verilog -include_pwr_gnd -remove_cells {{ {self.fill_cells} }} {self.output_netlist_filename}")
+        return True
+
+    # Copy and hack the klayout techfile, to add all required LEFs
+    def setup_klayout_techfile(self) -> bool:
+        source_path = Path(self.get_setting("par.inputs.klayout_techfile_source"))
+        if not source_path.exists():
+            raise FileNotFoundError(f"Klayout techfile not found: {source_path}")
+
+        dest_path = self.klayout_techfile_path
+
+        # klayout needs tlef + macro lefs
+        tech_lib_lefs = self.technology.read_libs([hammer_tech.filters.lef_filter], hammer_tech.HammerTechnologyUtils.to_plain_item, self.tech_lib_filter())
+        tech_lef = tech_lib_lefs[0]
+        extra_lib_lefs = self.technology.read_libs([hammer_tech.filters.lef_filter], hammer_tech.HammerTechnologyUtils.to_plain_item, self.extra_lib_filter())
+        
+        insert_lines=''
+        for lef_file in [tech_lef]+extra_lib_lefs:
+            insert_lines += f"<lef-files>{lef_file}</lef-files>\n"
+
+        with open(source_path, 'r') as sf:
+            with open(dest_path, 'w') as df:
+                self.logger.info("Modifying Klayout Techfile: {} -> {}".format
+                    (source_path, dest_path))
+                for line in sf:
+                    if '</lefdef>' in line:
+                        df.write(insert_lines)
+                    df.write(line)
+        return True
+
+    def write_gds(self) -> bool:
+        self.setup_klayout_techfile()
+
+        gds_files = self.technology.read_libs([
+            hammer_tech.filters.gds_filter
+        ], hammer_tech.HammerTechnologyUtils.to_plain_item)
+        if self.hierarchical_mode.is_nonleaf_hierarchical():
+            ilm_gds = list(map(lambda ilm: ilm.gds, self.get_input_ilms()))
+            gds_files.extend(ilm_gds)
+
+        tech_base_dir=self.get_setting('vlsi.builtins.hammer_vlsi_path')
+
+        self.block_append(f"""
+        # write gds
+        exec klayout -zz \\
+                -rd design_name={self.top_module} \\
+                -rd in_def={self.output_def_filename} \\
+                -rd in_files={" ".join(gds_files)} \\
+                -rd seal_file= \\
+                -rd tech_file={self.klayout_techfile_path} \\
+                -rd config_file= \\
+                -rd out_file={self.output_gds_filename} \\
+                -rm {tech_base_dir}/hammer_vlsi/vendor/def2stream.py
+        """) 
+        return True
+
+    def write_sdf(self) -> bool:
+        corners = self.get_mmmc_corners()  
+        self.append(f"write_sdf -corner setup {self.output_sdf_path}")
+        return True
+
+    def write_spefs(self) -> bool:
+        
         return True
 
     def write_regs(self) -> bool:
         # TODO: currently no analagous OpenROAD default script
         return True
 
+    def write_reports(self) -> bool:
+        # TODO: write all these to report files
+        self.block_append("""
+        ################################################################
+        # Final Report
+        report_checks -path_delay min_max -format full_clock_expanded -fields {input_pin slew capacitance} -digits 3
+        report_worst_slack -min -digits 3
+        report_worst_slack -max -digits 3
+        report_tns -digits 3
+        report_check_types -max_slew -max_capacitance -max_fanout -violators -digits 3
+        report_clock_skew -digits 3
+        report_power -corner "hold"
+
+        report_floating_nets -verbose
+        report_design_area
+
+        utl::metric "worst_slack_min" [sta::worst_slack -min]
+        utl::metric "worst_slack_max" [sta::worst_slack -max]
+        utl::metric "tns_max" [sta::total_negative_slack -max]
+        utl::metric "clock_skew" [sta::worst_clock_skew -setup]
+        utl::metric "max_slew_violations" [sta::max_slew_violation_count]
+        utl::metric "max_fanout_violations" [sta::max_fanout_violation_count]
+        utl::metric "max_capacitance_violations" [sta::max_capacitance_violation_count]
+        # report clock period as a metric for updating limits
+        utl::metric "clock_period" [get_property [lindex [all_clocks] 0] period]
+        """)
+        return True
+
     def write_design(self) -> bool:
-        # TODO: currently using OpenROAD's default finish script
-        self.cmds += [
-          "make DESIGN_CONFIG={conf} -f {make} finish".format(
-            conf=self.design_config_path(), 
-            make=self.openroad_flow_makefile_path()
-        )]
+        self.block_append(f"""
+        ################################################################
+        # Write Design
+        """)
+        self.append(f"write_def {self.output_def_filename}")
+        
+        # TODO: look at IR drop analysis from ~OpenROAD-flow-scripts/flow/scripts/final_report.tcl
+        # Static IR drop analysis
+
+        # Write netlist
+        self.write_netlist()
+
+        # GDS streamout.
+        self.write_gds()
+
+        # Write SDF
+        self.write_sdf()
+
+        # Make sure that generated-scripts exists.
+        os.makedirs(self.generated_scripts_dir, exist_ok=True)
+
+        self.write_reports()
+
+        self.ran_write_design=True
+
         return True
 
+    def generate_make_tracks(self) -> str:
+        output = []
+        # initialize_floorplan removes existing tracks 
+        #   --> use the make_tracks command to add routing tracks 
+        #       to a floorplan (with no arguments it uses default from tech LEF)
+        layers = self.get_setting("par.generate_power_straps_options.by_tracks.strap_layers")
+        for metal in self.get_stackup().metals:
+            output.append(f"make_tracks {metal.name}")
+        return "\n".join(output)
+
+    def create_floorplan_tcl(self) -> List[str]:
+        """
+        Create a floorplan TCL depending on the floorplan mode.
+        """
+        output = []  # type: List[str]
+
+        floorplan_mode = str(self.get_setting("par.openroad.floorplan_mode"))
+        if floorplan_mode == "manual":
+            floorplan_script_contents = str(self.get_setting("par.openroad.floorplan_script_contents"))
+            # TODO(edwardw): proper source locators/SourceInfo
+            output.append("# Floorplan manually specified from HAMMER")
+            output.extend(floorplan_script_contents.split("\n"))
+        elif floorplan_mode == "generate":
+            output.extend(self.generate_floorplan_tcl())
+        elif floorplan_mode == "auto":
+            output.append("# Using auto-generated floorplan")
+            output.append("plan_design")
+            spacing = self.get_setting("par.blockage_spacing")
+            bot_layer = self.get_stackup().get_metal_by_index(1).name
+            top_layer = self.get_setting("par.blockage_spacing_top_layer")
+            if top_layer is not None:
+                output.append("create_place_halo -all_blocks -halo_deltas {{{s} {s} {s} {s}}} -snap_to_site".format(
+                    s=spacing))
+                output.append("create_route_halo -all_blocks -bottom_layer {b} -space {s} -top_layer {t}".format(
+                    b=bot_layer, t=top_layer, s=spacing))
+        else:
+            if floorplan_mode != "blank":
+                self.logger.error("Invalid floorplan_mode {mode}. Using blank floorplan.".format(mode=floorplan_mode))
+            # Write blank floorplan
+            output.append("# Blank floorplan specified from HAMMER")
+        return output
+
+    @staticmethod
+    def generate_chip_size_constraint(width: Decimal, height: Decimal, left: Decimal, bottom: Decimal, right: Decimal,
+                                      top: Decimal, site: str) -> str:
+        """
+        Given chip width/height and margins, generate an OpenROAD TCL command to create the floorplan.
+        Also requires a technology specific name for the core site
+        """
+        return f"initialize_floorplan -site {site} -die_area {{ 0 0 {width} {height}}} -core_area {{{left} {bottom} {width-right-left} {height-top-bottom}}}"
+
+    def generate_floorplan_tcl(self) -> List[str]:
+        """
+        Generate a TCL floorplan for OpenROAD based on the input config/IR.
+        Not to be confused with create_floorplan_tcl, which calls this function.
+        """
+        output = []  # type: List[str]
+
+        # TODO(edwardw): proper source locators/SourceInfo
+        output.append("# Floorplan automatically generated from HAMMER")
+
+        # Top-level chip size constraint.
+        # Default/fallback constraints if no other constraints are provided.
+        # TODO snap this to a core site
+        chip_size_constraint = self.generate_chip_size_constraint(
+            site=self.technology.get_placement_site().name,
+            width=Decimal("1000"), height=Decimal("1000"),
+            left=Decimal("100"), bottom=Decimal("100"),
+            right=Decimal("100"), top=Decimal("100")
+        )
+
+        floorplan_constraints = self.get_placement_constraints()
+        global_top_layer = self.get_setting("par.blockage_spacing_top_layer") #  type: Optional[str]
+
+        ############## Actually generate the constraints ################
+        for constraint in floorplan_constraints:
+            # Floorplan names/insts need to not include the top-level module,
+            # despite the internal get_db commands including the top-level module...
+            # e.g. Top/foo/bar -> foo/bar
+            new_path = "/".join(constraint.path.split("/")[1:])
+
+            if new_path == "":
+                assert constraint.type == PlacementConstraintType.TopLevel, "Top must be a top-level/chip size constraint"
+                margins = constraint.margins
+                assert margins is not None
+                # Set top-level chip dimensions.
+                chip_size_constraint = self.generate_chip_size_constraint(
+                    site=self.technology.get_placement_site().name,
+                    width=constraint.width,
+                    height=constraint.height,
+                    left=margins.left,
+                    bottom=margins.bottom,
+                    right=margins.right,
+                    top=margins.top
+                )
+            else:
+                orientation = constraint.orientation if constraint.orientation is not None else "r0"
+                orientation = orientation.upper()
+                if constraint.create_physical:
+                    pass
+                if constraint.type == PlacementConstraintType.Dummy:
+                    pass
+                elif constraint.type == PlacementConstraintType.Placement:
+                    pass
+                # for OpenROAD
+                elif constraint.type in [PlacementConstraintType.HardMacro, PlacementConstraintType.Hierarchical]:
+                    output.append("place_cell -inst_name {inst} -origin {{ {x} {y} }} -orient {orientation} -status FIRM".format(
+                        inst=new_path,
+                        x=constraint.x,
+                        y=constraint.y,
+                        orientation=orientation,
+                    ))
+                    # TODO: add place_cell option [-status (PLACED|FIRM)]
+                    spacing = self.get_setting("par.blockage_spacing")
+                    if constraint.top_layer is not None:
+                        current_top_layer = constraint.top_layer #  type: Optional[str]
+                    elif global_top_layer is not None:
+                        current_top_layer = global_top_layer
+                    else:
+                        current_top_layer = None
+                    # TODO: find equivalent for place/route halo in OpenROAD
+
+                elif constraint.type == PlacementConstraintType.Obstruction:
+                    pass
+                else:
+                    assert False, "Should not reach here"
+        return [chip_size_constraint] + output
+
+    def specify_std_cell_power_straps(self, blockage_spacing: Decimal, bbox: Optional[List[Decimal]], nets: List[str]) -> List[str]:
+        """
+        Generate a list of TCL commands that build the low-level standard cell power strap rails.
+        This will use the -master option to create power straps based on technology.core.tap_cell_rail_reference.
+        The layer is set by technology.core.std_cell_rail_layer, which should be the highest metal layer in the std cell rails.
+
+        :param bbox: The optional (2N)-point bounding box of the area to generate straps. By default the entire core area is used.
+        :param nets: A list of power net names (e.g. ["VDD", "VSS"]). Currently only two are supported.
+        :return: A list of TCL commands that will generate power straps on rails.
+        """
+        layer_name = self.get_setting("technology.core.std_cell_rail_layer")
+        layer = self.get_stackup().get_metal(layer_name)
+        tcl=[]
+        tcl.append(f"add_pdn_stripe -grid {{grid}} -layer {{{layer.name}}} -width {0.48} -pitch {5.44} -offset 0 -followpins\n")
+        return []
+
+    def specify_power_straps(self, layer_name: str, bottom_via_layer_name: str, blockage_spacing: Decimal, pitch: Decimal, width: Decimal, spacing: Decimal, offset: Decimal, bbox: Optional[List[Decimal]], nets: List[str], add_pins: bool) -> List[str]:
+        """
+        Generate a list of TCL commands that will create power straps on a given layer.
+        This is a low-level, cad-tool-specific API. It is designed to be called by higher-level methods, so calling this directly is not recommended.
+        This method assumes that power straps are built bottom-up, starting with standard cell rails.
+
+        :param layer_name: The layer name of the metal on which to create straps.
+        :param bottom_via_layer_name: The layer name of the lowest metal layer down to which to drop vias.
+        :param blockage_spacing: The minimum spacing between the end of a strap and the beginning of a macro or blockage.
+        :param pitch: The pitch between groups of power straps (i.e. from left edge of strap A to the next left edge of strap A).
+        :param width: The width of each strap in a group.
+        :param spacing: The spacing between straps in a group.
+        :param offset: The offset to start the first group.
+        :param bbox: The optional (2N)-point bounding box of the area to generate straps. By default the entire core area is used.
+        :param nets: A list of power nets to create (e.g. ["VDD", "VSS"], ["VDDA", "VSS", "VDDB"],  ... etc.).
+        :param add_pins: True if pins are desired on this layer; False otherwise.
+        :return: A list of TCL commands that will generate power straps.
+        """
+        return [f" {layer_name} {{width {width} pitch {pitch} offset {offset}}} \n"]
+    
+    def process_sdc_file(self,post_synth_sdc) -> str:
+        # overwrite SDC file to exclude group_path command
+        # change units in SDC file (1000.0fF and 1000.0ps cause errors)
+        sdc_filename=os.path.basename(post_synth_sdc)
+        new_post_synth_sdc = f"{self.run_dir}/{sdc_filename}"
+        with open(post_synth_sdc,'r') as f_old:
+            lines=f_old.readlines()
+            with open(new_post_synth_sdc,'w') as f_new:
+                i = 0
+                while i < len(lines):
+                    line = lines[i]
+                    words = line.strip().split()
+                    if line.startswith("set_units") and len(words) >= 3:
+                        unit_type = words[1]
+                        value=words[2].split('.')[0]
+                        units=words[2].replace('.','')
+                        for c in value:
+                            if not c.isnumeric():
+                                value=value.replace(c,'')
+                        for c in units:
+                            if c.isnumeric():
+                                units=units.replace(c,'')
+                        if value == '1000' and len(units) >= 2:
+                            value='1'
+                            units=self.scale_units_1000x_down(units[0])+units[1:]
+                        line=f"set_units {words[1]} {value}{units}\n"
+
+                    if line.startswith("group_path"):
+                        while (lines[i].strip().endswith('\\') and i < len(lines)-1):
+                            i=i+1
+                    else:
+                        f_new.write(line)
+                    i=i+1
+        return new_post_synth_sdc
+
+    def generate_sdc_files(self) -> List[str]:
+        sdc_files = []  # type: List[str]
+
+        # Generate constraints
+        clock_constraints_fragment = os.path.join(self.run_dir, "clock_constraints_fragment.sdc")
+        with open(clock_constraints_fragment, "w") as f:
+            f.write(self.sdc_clock_constraints)
+        sdc_files.append(clock_constraints_fragment)
+
+        # Generate port constraints.
+        pin_constraints_fragment = os.path.join(self.run_dir, "pin_constraints_fragment.sdc")
+        with open(pin_constraints_fragment, "w") as f:
+            f.write(self.sdc_pin_constraints)
+        sdc_files.append(pin_constraints_fragment)
+
+        # Add the post-synthesis SDC, if present.
+        post_synth_sdc = self.post_synth_sdc
+        if post_synth_sdc is not None:
+            self.post_synth_sdc = self.process_sdc_file(self.post_synth_sdc)
+            sdc_files.append(self.post_synth_sdc)
+
+        return sdc_files
+
+def openroad_global_settings(ht: HammerTool) -> bool:
+    """Settings that need to be reapplied at every tool invocation"""
+    assert isinstance(ht, HammerPlaceAndRouteTool)
+    assert isinstance(ht, OpenROADTool)
+    assert isinstance(ht, TCLTool)
+    ht.create_enter_script()
+
+    # Generic settings
+    ht.append("# OpenROAD TCL Script")
+
+    return True
+
 tool = OpenROADPlaceAndRoute
diff --git a/src/hammer-vlsi/par/openroad/defaults.yml b/src/hammer-vlsi/par/openroad/defaults.yml
index eb5771940..7e18daf5c 100644
--- a/src/hammer-vlsi/par/openroad/defaults.yml
+++ b/src/hammer-vlsi/par/openroad/defaults.yml
@@ -2,6 +2,8 @@
 # The values specified in this file are the defaults.
 
 par.openroad:
+  # Location of the binary.
+  openroad_bin: "openroad"
   # this is just to make hammer happy (although, it is never called AFAICT).
   # the version is actually calculated by the OpenROADTool class
   version: "dummy-value"
@@ -9,3 +11,13 @@ par.openroad:
   # Generate the TCL file but do not run it yet.
   generate_only: false
 
+  # Floorplanning SDC constraints to use.
+  # Valid options are:
+  # - blank - Specify no floorplanning constraints (likely won't work)
+  # - manual - Specify the contents of a manual SDC floorplanning script to use in floorplan_script_contents.
+  # - generate - Generate a floorplan from the hammer-vlsi config/IR.
+  # - auto - Use Innovus's `plan_design` command to auto-generate a floorplan. Will have poor, but sane results
+  #   If you specify this, you must also specify the floorplanning_script config below. The floorplanning_script config will be ignored in other modes.
+  # TODO(edwardw): Put in new floorplanning thing here when done.
+  floorplan_mode: generate
+  floorplan_script_contents: null
\ No newline at end of file
diff --git a/src/hammer-vlsi/synthesis/yosys/__init__.py b/src/hammer-vlsi/synthesis/yosys/__init__.py
index bfd8fa4d4..e17aa89b7 100644
--- a/src/hammer-vlsi/synthesis/yosys/__init__.py
+++ b/src/hammer-vlsi/synthesis/yosys/__init__.py
@@ -2,20 +2,34 @@
 # -*- coding: utf-8 -*-
 #
 # Yosys synthesis plugin for Hammer (as part of OpenROAD-flow installation).
+# mostly adoped from OpenLANE scripts found here: https://github.com/The-OpenROAD-Project/OpenLane/tree/master/scripts
 #
 # See LICENSE for licence details.
 
-import os
 from textwrap import dedent as dd
-from typing import List, Optional, Dict, Any
 
+# from hammer_utils import deepdict
+from hammer_vlsi.vendor import OpenROADTool, OpenROADSynthesisTool
+
+###########
+from hammer_vlsi import HammerTool, HammerToolStep, HammerToolHookAction, HierarchicalMode, TCLTool
+from hammer_vlsi.constraints import MMMCCorner, MMMCCornerType
+from hammer_utils import VerilogUtils, optional_map
+from hammer_vlsi import HammerSynthesisTool
 from hammer_logging import HammerVLSILogging
-from hammer_utils import deepdict
-from hammer_vlsi import HammerToolStep
-from hammer_vlsi.vendor import OpenROADSynthesisTool
+from hammer_vlsi import MMMCCornerType
+import hammer_tech
 
+from typing import Dict, List, Any, Optional, Callable
 
-class YosysSynth(OpenROADSynthesisTool):
+import specialcells
+from specialcells import CellType, SpecialCell
+
+import os
+import json
+from collections import Counter
+
+class YosysSynth(HammerSynthesisTool, OpenROADTool, TCLTool):
 
     #=========================================================================
     # overrides from parent classes
@@ -29,6 +43,7 @@ def steps(self) -> List[HammerToolStep]:
             self.add_tieoffs,
             self.write_regs,
             self.generate_reports,
+            self.write_outputs
         ])
 
     def do_post_steps(self) -> bool:
@@ -37,20 +52,56 @@ def do_post_steps(self) -> bool:
 
     @property
     def env_vars(self) -> Dict[str, str]:
-        new_dict = deepdict(super().env_vars)
-        new_dict.update({}) # TODO: stuffs
+        new_dict = dict(super().env_vars)
+        new_dict["YOSYS_BIN"] = self.get_setting("synthesis.yosys.yosys_bin")
         return new_dict
 
-    def fill_outputs(self) -> bool:
-        # mapped verilog files
-        self.output_files = [self.mapped_v_path()]
-        # mapped sdc
-        self.output_sdc = self.mapped_sdc_path()
+    @property
+    def post_synth_sdc(self) -> Optional[str]:
+        # No post-synth SDC input for synthesis...
+        return None
+    
+    @property
+    def all_regs_path(self) -> str:
+        return os.path.join(self.run_dir, "find_regs_paths.json")
 
+    @property
+    def all_cells_path(self) -> str:
+        return os.path.join(self.run_dir, "find_regs_cells.json")
+    
+    def fill_outputs(self) -> bool:
         # TODO: actually generate the following for simulation
-        self.output_all_regs = ""
-        self.output_seq_cells = ""
-        self.sdf_file = ""
+        # Check that the regs paths were written properly if the write_regs step was run
+        self.output_seq_cells = self.all_cells_path
+        self.output_all_regs = self.all_regs_path
+        if self.ran_write_regs:
+            if not os.path.isfile(self.all_cells_path):
+                raise ValueError("Output find_regs_cells.json %s not found" % (self.all_cells_path))
+
+            if not os.path.isfile(self.all_regs_path):
+                raise ValueError("Output find_regs_paths.json %s not found" % (self.all_regs_path))
+
+            # if not self.process_reg_paths(self.all_regs_path):
+            #     self.logger.error("Failed to process all register paths")
+        else:
+            self.logger.info("Did not run write_regs")
+
+        # Check that the synthesis outputs exist if the synthesis run was successful
+        mapped_v = self.mapped_v_path
+        self.output_files = [mapped_v]
+        self.output_sdc = self.mapped_sdc_path
+        # self.sdf_file = self.output_sdf_path
+        if self.ran_write_outputs:
+            if not os.path.isfile(mapped_v):
+                raise ValueError("Output mapped verilog %s not found" % (mapped_v)) # better error?
+
+            if not os.path.isfile(self.mapped_sdc_path):
+                raise ValueError("Output SDC %s not found" % (self.mapped_sdc_path)) # better error?
+
+            # if not os.path.isfile(self.output_sdf_path):
+            #     raise ValueError("Output SDF %s not found" % (self.output_sdf_path))
+        else:
+            self.logger.info("Did not run write_outputs")
 
         return True
 
@@ -59,7 +110,7 @@ def export_config_outputs(self) -> Dict[str, Any]:
         outputs["synthesis.outputs.sdc"] = self.output_sdc
         outputs["synthesis.outputs.seq_cells"] = self.output_seq_cells
         outputs["synthesis.outputs.all_regs"] = self.output_all_regs
-        outputs["synthesis.outputs.sdf_file"] = self.sdf_file
+        outputs["synthesis.outputs.sdf_file"] = ""
         return outputs
 
     def tool_config_prefix(self) -> str:
@@ -68,88 +119,225 @@ def tool_config_prefix(self) -> str:
     #=========================================================================
     # useful subroutines
     #=========================================================================
+    @property
     def mapped_v_path(self) -> str:
-        return os.path.join(self.run_dir, 
-          "results/{}/{}/1_synth.v".format(
-            self.get_setting("vlsi.core.technology"),
-            self.top_module))
+        return os.path.join(self.run_dir, "{}.mapped.v".format(self.top_module))
 
+    @property
     def mapped_sdc_path(self) -> str:
-        return os.path.join(self.run_dir,
-          "results/{}/{}/1_synth.sdc".format(
-            self.get_setting("vlsi.core.technology"),
-            self.top_module))
+        return os.path.join(self.run_dir, "{}.mapped.sdc".format(self.top_module))
+    
+    @property
+    def mapped_blif_path(self) -> str:
+        return os.path.join(self.run_dir, "{}.mapped.blif".format(self.top_module))
+
+    def get_timing_libs(self, corner: Optional[MMMCCorner] = None) -> str:
+        """
+        Helper function to get the list of ASCII timing .lib files in space separated format.
+
+        :param corner: Optional corner to consider. If supplied, this will use filter_for_mmmc to select libraries that
+        match a given corner (voltage/temperature).
+        :return: List of lib files separated by spaces
+        """
+        pre_filters = optional_map(corner, lambda c: [self.filter_for_mmmc(voltage=c.voltage,
+                                                                           temp=c.temp)])  # type: Optional[List[Callable[[hammer_tech.Library],bool]]]
 
-    def synth_script_path(self) -> str:
-        # TODO: generate this internally
-        openroad = self.openroad_flow_path()
-        return os.path.join(openroad, "flow/scripts/synth.tcl")
+        lib_args = self.technology.read_libs([hammer_tech.filters.timing_lib_with_ecsm_filter],
+                                             hammer_tech.HammerTechnologyUtils.to_plain_item,
+                                             extra_pre_filters=pre_filters)
+        
+        return " ".join(lib_args)
 
     def run_yosys(self) -> bool:
-        run_script    = os.path.join(self.run_dir, "run.sh")
-        makefile      = self.openroad_flow_makefile_path()
-        design_config = self.design_config_path()
-        synth_script  = self.synth_script_path()
-
-        self.validate_openroad_installation()
-        self.setup_openroad_rundir()
-        self.create_design_config()
-
-        with open(run_script, "w") as f:
-            f.write(dd("""\
-              #!/bin/bash
-              cd "{rundir}"
-              mkdir -p results/{tech}/{name}
-              make DESIGN_CONFIG={conf} SYNTH_SCRIPT={script} -f {make} synth\
-            """.format(
-              rundir=self.run_dir,
-              tech=self.get_setting("vlsi.core.technology"),
-              name=self.top_module, 
-              conf=design_config, 
-              script=synth_script,
-              make=makefile
-            )))
-        os.chmod(run_script, 0o755)
+        """Close out the synthesis script and run Yosys."""
+        # Quit Yosys.
+        self.append("exit")
+
+        # Create synthesis script.
+        syn_tcl_filename = os.path.join(self.run_dir, "syn.tcl")
+
+        with open(syn_tcl_filename, "w") as f:
+            f.write("\n".join(self.output))
+
+        # Build args.
+        args = [
+            self.get_setting("synthesis.yosys.yosys_bin"),
+            "-c", syn_tcl_filename,
+        ]
 
         if bool(self.get_setting("synthesis.yosys.generate_only")):
-            self.logger.info("Generate-only mode: command-line is " + \
-                             " ".join(args))
+            self.logger.info("Generate-only mode: command-line is " + " ".join(args))
         else:
-            # Temporarily disable colors/tag to make run output more readable
+            # Temporarily disable colours/tag to make run output more readable.
             # TODO: think of a more elegant way to do this?
             HammerVLSILogging.enable_colour = False
             HammerVLSILogging.enable_tag = False
-            self.run_executable([run_script]) # TODO: check for errors
+            self.run_executable(args, cwd=self.run_dir) # TODO: check for errors and deal with them
             HammerVLSILogging.enable_colour = True
             HammerVLSILogging.enable_tag = True
 
         return True
 
+    def write_sdc_file(self) -> bool:
+        with open(self.mapped_sdc_path,'w') as f:
+            # custom sdc constraints
+            if self.driver_cell is not None:
+                f.write(f"set_driving_cell {self.driver_cell}\n" )
+                f.write("set_load 5\n")
+        return True
+
+    def block_append(self,commands) -> bool:
+        for line in commands.split('\n'):
+            self.append(line.strip())
+        return True
+
     #========================================================================
     # synthesis main steps
     #========================================================================
     def init_environment(self) -> bool:
-        # TODO: currently using OpenROAD's default synthesis script
+
+        # set variables to match global variables in OpenLANE
+        time_unit = self.get_time_unit().value_prefix + self.get_time_unit().unit
+        clock_port = self.get_clock_ports()[0]
+        self.clock_port_name = clock_port.name
+        time_unit = "ps" # yosys requires time units in ps
+        self.clock_period = int(clock_port.period.value_in_units(time_unit))
+        self.clock_uncertainty = int(clock_port.period.value_in_units(time_unit))
+        self.clock_transition = 0.15 # SYNTH_CLOCK_TRANSITION
+
+        self.synth_cap_load = 33.5 # SYNTH_CAP_LOAD
+        self.max_fanout = 5 # default SYNTH_MAX_FANOUT = 5
+
+        self.driver_cell = None
+        driver_cells = self.technology.get_special_cell_by_type(CellType.Driver)
+        if driver_cells is None or driver_cells[0].input_ports is None or driver_cells[0].output_ports is None:
+            self.logger.warning("Driver cells and their input and output ports are unspecified and will not be added during synthesis.")
+        else:
+            self.driver_cell = driver_cells[0].name[0]
+            self.driver_ports_in = driver_cells[0].input_ports[0]
+            self.driver_ports_out = driver_cells[0].output_ports[0]
+        # Yosys commands only take a single lib file
+        #   so use typical corner liberty file
+        corners = self.get_mmmc_corners()  # type: List[MMMCCorner]
+        corner_tt = next((corner for corner in corners if corner.type == MMMCCornerType.Extra), None)
+        self.liberty_file = self.get_timing_libs(corner_tt)
+        
+        self.append("yosys -import")
+
+        # replaces undef (x) constants with defined (0/1) constants.
+        self.append("setundef -zero")
+
+        # We are switching working directories and Yosys still needs to find paths.
+        abspath_input_files = list(map(lambda name: os.path.join(os.getcwd(), name), self.input_files))  # type: List[str]
+        
+        # Add any verilog_synth wrappers (which are needed in some technologies e.g. for SRAMs) which need to be
+        # synthesized.
+        abspath_input_files += self.technology.read_libs([
+            hammer_tech.filters.verilog_synth_filter
+        ], hammer_tech.HammerTechnologyUtils.to_plain_item)
+        
+        for verilog_file in abspath_input_files:
+            self.append(f"read_verilog -sv {verilog_file}")
+
+        liberty_files = self.technology.read_libs([hammer_tech.filters.timing_lib_with_ecsm_filter], hammer_tech.HammerTechnologyUtils.to_plain_item)
+        for lib_file in liberty_files:
+            self.append(f"read_liberty -lib {lib_file}")
+
         return True
 
     def syn_generic(self) -> bool:
-        # TODO: currently using OpenROAD's default synthesis script
+        self.block_append(f"""
+        # TODO: verify this command, it was in yosys manual but not in OpenLANE script
+        yosys proc
+        hierarchy -check -top {self.top_module}
+
+        synth -top {self.top_module}
+
+        # Optimize the design
+        opt -purge
+
+        dfflibmap -liberty {self.liberty_file}
+
+        opt
+        """)
+        # need this report??
+        # tee -o "{self.run_dir}/{self.top_module}_pre.stat" stat
+
+        # merges shareable resources into a single resource. A SAT solver
+        # is used to determine if two resources are share-able.
+        # self.append('share -aggressive')
+
+        self.write_sdc_file()
         return True
 
     def syn_map(self) -> bool:
-        # TODO: currently using OpenROAD's default synthesis script
+        self.block_append(f"""
+        # Technology mapping for cells
+        # ABC supports multiple liberty files, but the hook from Yosys to ABC doesn't
+        puts "second abc pass"
+        abc -D {self.clock_period} \
+            -constr "{self.mapped_sdc_path}" \
+            -liberty "{self.liberty_file.split()[0]}" \
+            -nocleanup \
+            -showtmp
+
+        # Replace undef values with defined constants
+        # TODO: do we need this??
+        setundef -zero
+
+        # TODO: what is the purpose of splitnets?
+        # Splitting nets resolves unwanted compound assign statements in netlist (assign [..] = [..])
+        splitnets
+
+        # Remove unused cells and wires
+        opt_clean -purge
+        """)
         return True
 
     def add_tieoffs(self) -> bool:
-        # TODO: currently using OpenROAD's default synthesis script
+        tie_hi_cells = self.technology.get_special_cell_by_type(CellType.TieHiCell)
+        tie_lo_cells = self.technology.get_special_cell_by_type(CellType.TieLoCell)
+        tie_hilo_cells = self.technology.get_special_cell_by_type(CellType.TieHiLoCell)
+
+        if len(tie_hi_cells) != 1 or len (tie_lo_cells) != 1 or tie_hi_cells[0].input_ports is None or tie_lo_cells[0].input_ports is None:
+            self.logger.warning("Hi and Lo tiecells and their input ports are unspecified or improperly specified and will not be added during synthesis.")
+        else:   
+            tie_hi_cell = tie_hi_cells[0].name[0]
+            tie_hi_port = tie_hi_cells[0].input_ports[0]
+            tie_lo_cell = tie_lo_cells[0].name[0]
+            tie_lo_port = tie_lo_cells[0].input_ports[0]
+
+            self.block_append(f"""
+            # Technology mapping of constant hi- and/or lo-drivers
+            hilomap -hicell "{tie_hi_cell} {tie_hi_port}" -locell "{tie_lo_cell} {tie_lo_port}"
+            """)
+        if self.driver_cell is not None:
+            self.block_append(f"""
+            # Insert driver cells for pass through wires
+            insbuf "{self.driver_cell} {self.driver_ports_in} {self.driver_ports_out}"
+            """)
         return True
 
     def write_regs(self) -> bool:
-        # TODO: currently using OpenROAD's default synthesis script
+        # TODO: generate find_regs_cells.json / find_regs_paths.json here
+        self.ran_write_regs = False
         return True
 
     def generate_reports(self) -> bool:
-        # TODO: currently using OpenROAD's default synthesis script
+        # TODO: generate all reports (will probably need to parse the log file)
+        self.append(f'tee -o "{self.run_dir}/{self.top_module}.synth_check.rpt" check')
+        self.append(f'tee -o "{self.run_dir}/{self.top_module}.synth_stat.txt" stat -top {self.top_module} -liberty {self.liberty_file}')
         return True
-
+    
+    def write_outputs(self) -> bool:
+        self.append(f'write_verilog -noattr -noexpr -nohex -nodec -defparam "{self.mapped_v_path}"')
+        self.append("flatten")
+        mapped_flat_v_path=os.path.join(self.run_dir, f"{self.top_module}.mapped.flat.v")
+        self.append(f'write_verilog -noattr -noexpr -nohex -nodec -defparam "{mapped_flat_v_path}"')
+        # BLIF file seems to be easier to parse than mapped verilog for find_regs functions so leave for now
+        self.append(f'write_blif -top {self.top_module} "{self.mapped_blif_path}"')
+        # TODO: figure out why the OpenLANE script re-runs synthesis & flattens design, when we explicitly requested hierarchical mode
+        self.ran_write_outputs = True
+        return True
+    
 tool = YosysSynth
diff --git a/src/hammer-vlsi/synthesis/yosys/defaults.yml b/src/hammer-vlsi/synthesis/yosys/defaults.yml
index 8dffdef1e..f0d7f7a0b 100644
--- a/src/hammer-vlsi/synthesis/yosys/defaults.yml
+++ b/src/hammer-vlsi/synthesis/yosys/defaults.yml
@@ -2,9 +2,12 @@
 # The values specified in this file are the defaults.
 
 synthesis.yosys:
+  # Location of the binary.
+  yosys_bin: "yosys"
   # this is just to make hammer happy (although, it is never called AFAICT).
   # the version is actually calculated by the OpenROADTool class
   version: "dummy-value"
 
   # Generate the input scripts but don't run anything
-  generate_only: false
+  # TODO: make this tool-independent?
+  generate_only: false
\ No newline at end of file
diff --git a/src/hammer-vlsi/technology/sky130/__init__.py b/src/hammer-vlsi/technology/sky130/__init__.py
index 81ce5356f..6d5cd503f 100644
--- a/src/hammer-vlsi/technology/sky130/__init__.py
+++ b/src/hammer-vlsi/technology/sky130/__init__.py
@@ -27,12 +27,13 @@ def post_install_script(self) -> None:
         self.library_name = 'sky130_fd_sc_hd'
         # check whether variables were overriden to point to a valid path
         self.use_openram = os.path.exists(self.get_setting("technology.sky130.openram_lib"))
-        self.use_nda_files = (os.path.exists(self.get_setting("technology.sky130.sky130_nda")))
-        self.setup_sram_cdl()
+        self.use_nda_files = os.path.exists(self.get_setting("technology.sky130.sky130_nda"))
         self.setup_cdl()
         self.setup_verilog()
         self.setup_techlef()
         self.setup_lvs_deck()
+        self.setup_sram_spice()
+        self.setup_sram_lef()
         print('Loaded Sky130 Tech')
 
 
@@ -40,22 +41,22 @@ def post_install_script(self) -> None:
     def setup_cdl(self) -> None:
         setting_dir = self.get_setting("technology.sky130.sky130A")
         setting_dir = Path(setting_dir)
-        cdl_old_path = setting_dir / 'libs.ref' / self.library_name / 'cdl' / f'{self.library_name}.cdl'
-        if not cdl_old_path.exists():
-            raise FileNotFoundError(f"CDL not found: {cdl_old_path}")
+        source_path = setting_dir / 'libs.ref' / self.library_name / 'cdl' / f'{self.library_name}.cdl'
+        if not source_path.exists():
+            raise FileNotFoundError(f"CDL not found: {source_path}")
 
         cache_tech_dir_path = Path(self.cache_dir)
         os.makedirs(cache_tech_dir_path, exist_ok=True)
-        cdl_new_path = cache_tech_dir_path / f'{self.library_name}.cdl'
+        dest_path = cache_tech_dir_path / f'{self.library_name}.cdl'
 
-        f_old = open(cdl_old_path,'r')
-        f_new = open(cdl_new_path,'w')
-        for line in f_old:
-            line = line.replace('pfet_01v8_hvt','phighvt')
-            line = line.replace('nfet_01v8',    'nshort')
-            f_new.write(line)
-        f_old.close()
-        f_new.close()
+        with open(source_path, 'r') as sf:
+            with open(dest_path, 'w') as df:
+                self.logger.info("Modifying CDL netlist: {} -> {}".format
+                    (source_path, dest_path))
+                for line in sf:
+                    line = line.replace('pfet_01v8_hvt', 'phighvt')
+                    line = line.replace('nfet_01v8'    , 'nshort')
+                    df.write(line)
 
     # Copy and hack the verilog
     #   - <library_name>.v: remove 'wire 1' and one endif line to fix syntax errors
@@ -66,60 +67,60 @@ def setup_verilog(self) -> None:
         setting_dir = Path(setting_dir)
 
         # <library_name>.v
-        verilog_old_path = setting_dir / 'libs.ref' / self.library_name / 'verilog' / f'{self.library_name}.v'
-        if not verilog_old_path.exists():
-            raise FileNotFoundError(f"Verilog not found: {verilog_old_path}")
+        source_path = setting_dir / 'libs.ref' / self.library_name / 'verilog' / f'{self.library_name}.v'
+        if not source_path.exists():
+            raise FileNotFoundError(f"Verilog not found: {source_path}")
 
         cache_tech_dir_path = Path(self.cache_dir)
         os.makedirs(cache_tech_dir_path, exist_ok=True)
-        verilog_new_path = cache_tech_dir_path / f'{self.library_name}.v'
+        dest_path = cache_tech_dir_path / f'{self.library_name}.v'
 
-        f_old = open(verilog_old_path,'r')
-        f_new = open(verilog_new_path,'w')
-        for line in f_old:
-            line = line.replace('wire 1','// wire 1')
-            line = line.replace('`endif SKY130_FD_SC_HD__LPFLOW_BLEEDER_FUNCTIONAL_V','`endif // SKY130_FD_SC_HD__LPFLOW_BLEEDER_FUNCTIONAL_V')
-            f_new.write(line)
-        f_old.close()
-        f_new.close()
+        with open(source_path, 'r') as sf:
+            with open(dest_path, 'w') as df:
+                self.logger.info("Modifying Verilog netlist: {} -> {}".format
+                    (source_path, dest_path))
+                for line in sf:
+                    line = line.replace('wire 1','// wire 1')
+                    line = line.replace('`endif SKY130_FD_SC_HD__LPFLOW_BLEEDER_FUNCTIONAL_V','`endif // SKY130_FD_SC_HD__LPFLOW_BLEEDER_FUNCTIONAL_V')
+                    df.write(line)
 
         # primitives.v
-        verilog_old_path = setting_dir / 'libs.ref' / self.library_name / 'verilog' / 'primitives.v'
-        if not verilog_old_path.exists():
-            raise FileNotFoundError(f"Verilog not found: {verilog_old_path}")
+        source_path = setting_dir / 'libs.ref' / self.library_name / 'verilog' / 'primitives.v'
+        if not source_path.exists():
+            raise FileNotFoundError(f"Verilog not found: {source_path}")
 
         cache_tech_dir_path = Path(self.cache_dir)
         os.makedirs(cache_tech_dir_path, exist_ok=True)
-        verilog_new_path = cache_tech_dir_path / 'primitives.v'
+        dest_path = cache_tech_dir_path / 'primitives.v'
 
-        f_old = open(verilog_old_path,'r')
-        f_new = open(verilog_new_path,'w')
-        for line in f_old:
-            line = line.replace('`default_nettype none','`default_nettype wire')
-            f_new.write(line)
-        f_old.close()
-        f_new.close()
+        with open(source_path, 'r') as sf:
+            with open(dest_path, 'w') as df:
+                self.logger.info("Modifying Verilog netlist: {} -> {}".format
+                    (source_path, dest_path))
+                for line in sf:
+                    line = line.replace('`default_nettype none','`default_nettype wire')
+                    df.write(line)
 
     # Copy and hack the tech-lef, adding this very important `licon` section
     def setup_techlef(self) -> None:
         setting_dir = self.get_setting("technology.sky130.sky130A")
         setting_dir = Path(setting_dir)
-        tlef_old_path = setting_dir / 'libs.ref' / self.library_name / 'techlef' / f'{self.library_name}.tlef'
-        if not tlef_old_path.exists():
-            raise FileNotFoundError(f"Tech-LEF not found: {tlef_old_path}")
+        source_path = setting_dir / 'libs.ref' / self.library_name / 'techlef' / f'{self.library_name}__nom.tlef'
+        if not source_path.exists():
+            raise FileNotFoundError(f"Tech-LEF not found: {source_path}")
 
         cache_tech_dir_path = Path(self.cache_dir)
         os.makedirs(cache_tech_dir_path, exist_ok=True)
-        tlef_new_path = cache_tech_dir_path / f'{self.library_name}.tlef'
+        dest_path = cache_tech_dir_path / f'{self.library_name}__nom.tlef'
 
-        f_old = open(tlef_old_path,'r')
-        f_new = open(tlef_new_path,'w')
-        for line in f_old:
-            f_new.write(line)
-            if line.strip() == 'END pwell':
-                f_new.write(_the_tlef_edit)
-        f_old.close()
-        f_new.close()
+        with open(source_path, 'r') as sf:
+            with open(dest_path, 'w') as df:
+                self.logger.info("Modifying Technology LEF: {} -> {}".format
+                    (source_path, dest_path))
+                for line in sf:
+                    df.write(line)
+                    if line.strip() == 'END pwell':
+                        df.write(_the_tlef_edit)
 
     # Remove conflicting specification statements found in PDK LVS decks
     def setup_lvs_deck(self) -> None:
@@ -146,13 +147,17 @@ def setup_lvs_deck(self) -> None:
                         (source_path, dest_path))
                     df.write(matcher.sub("", sf.read()))
                     df.write(LVS_DECK_INSERT_LINES)
-
+    
     def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]:
-        hooks = {"innovus": [
+        hooks = {
+            "openroad": [
+            HammerTool.make_pre_insertion_hook("place_opt_design",   sky130_set_wire_rc)
+            ],
+            "innovus": [
             HammerTool.make_post_insertion_hook("init_design",      sky130_innovus_settings),
             HammerTool.make_pre_insertion_hook("place_tap_cells",   sky130_add_endcaps),
-            HammerTool.make_pre_insertion_hook("power_straps",      sky130_power_nets),
-            HammerTool.make_pre_insertion_hook("write_design",      sky130_connect_nets)
+            HammerTool.make_pre_insertion_hook("power_straps",      sky130_connect_nets),
+            HammerTool.make_pre_insertion_hook("write_design",      sky130_connect_nets2)
             ]}
         return hooks.get(tool_name, [])
 
@@ -180,20 +185,45 @@ def openram_sram_names() -> List[str]:
             "sky130_sram_2kbyte_1rw1r_32x512_8"
         ]
 
-    def setup_sram_cdl(self) -> None:
+    def setup_sram_spice(self) -> None:
         if not self.use_openram: return
         for sram_name in self.openram_sram_names():
-            old_path = Path(self.get_setting("technology.sky130.openram_lib")) / sram_name / f"{sram_name}.lvs.sp"
-            new_path = self.expand_tech_cache_path(f'tech-sky130-cache/{sram_name}/{sram_name}.lvs.sp')
-            if not old_path.exists():
-                raise FileNotFoundError(f"SRAM CDL file not found: {old_path}")
-            self.ensure_dirs_exist(new_path)
-            with open(old_path,'r') as f_old:
-                with open(new_path,'w') as f_new:
-                    for line in f_old:
+            source_path = Path(self.get_setting("technology.sky130.openram_lib")) / sram_name / f"{sram_name}.lvs.sp"
+            dest_path = self.expand_tech_cache_path(f'tech-sky130-cache/{sram_name}/{sram_name}.lvs.sp')
+            if not source_path.exists():
+                raise FileNotFoundError(f"SRAM Spice file not found: {source_path}")
+            self.ensure_dirs_exist(dest_path)
+            with open(source_path,'r') as sf:
+                with open(dest_path,'w') as df:
+                    self.logger.info("Modifying SRAM SPICE deck: {} -> {}".format
+                        (source_path, dest_path))
+                    for line in sf:
                         line = line.replace('sky130_fd_pr__pfet_01v8','pshort')
                         line = line.replace('sky130_fd_pr__nfet_01v8','nshort')
-                        f_new.write(line)
+                        line = line.replace('wmask0[0]'    , 'wmask0')
+                        df.write(line)
+
+    def setup_sram_lef(self) -> None:
+        if not self.use_openram: return
+        for sram_name in self.openram_sram_names():
+            source_path = Path(self.get_setting("technology.sky130.openram_lib")) / sram_name / f"{sram_name}.lef"
+            dest_path = self.expand_tech_cache_path(f'tech-sky130-cache/{sram_name}/{sram_name}.lef')
+            if not source_path.exists():
+                raise FileNotFoundError(f"SRAM LEF file not found: {source_path}")
+            self.ensure_dirs_exist(dest_path)
+            with open(source_path,'r') as sf:
+                with open(dest_path,'w') as df:
+                    self.logger.info("Modifying SRAM LEF deck: {} -> {}".format
+                        (source_path, dest_path))
+                    units=False
+                    for line in sf:
+                        if line.strip().startswith("UNITS"):
+                            units=True
+                        if line.strip().startswith("END UNITS"):
+                            units=False
+                            continue
+                        if not units:
+                            df.write(line)
     ''' <<<<<<< END OpenRAM SRAM-specific functions '''
 
 _the_tlef_edit = '''
@@ -272,23 +302,20 @@ def sky130_innovus_settings(ht: HammerTool) -> bool:
     )
     return True
 
-# Pair VDD/VPWR and VSS/VGND nets
-#   these commands are already added in Innovus.write_netlist,
-#   but must also occur before power straps are placed
-def sky130_power_nets(ht: HammerTool) -> bool:
-    assert isinstance(ht, HammerPlaceAndRouteTool), "Innovus settings only for par"
-    assert isinstance(ht, TCLTool), "innovus settings can only run on TCL tools"
-    for pwr_gnd_net in (ht.get_all_power_nets() + ht.get_all_ground_nets()):
-            if pwr_gnd_net.tie is not None:
-                ht.append("connect_global_net {tie} -type net -net_base_name {net}".format(tie=pwr_gnd_net.tie, net=pwr_gnd_net.name))
-    return True
-
 def sky130_connect_nets(ht: HammerTool) -> bool:
     assert isinstance(ht, HammerPlaceAndRouteTool), "connect global nets only for par"
     assert isinstance(ht, TCLTool), "connect global nets can only run on TCL tools"
     for pwr_gnd_net in (ht.get_all_power_nets() + ht.get_all_ground_nets()):
             if pwr_gnd_net.tie is not None:
-                ht.append("connect_global_net {tie} -type pg_pin -pin_base_name {net} -all".format(tie=pwr_gnd_net.tie, net=pwr_gnd_net.name))
+                ht.append("connect_global_net {tie} -type pg_pin -pin_base_name {net} -all -auto_tie -netlist_override".format(tie=pwr_gnd_net.tie, net=pwr_gnd_net.name))
+                ht.append("connect_global_net {tie} -type net    -net_base_name {net} -all -netlist_override".format(tie=pwr_gnd_net.tie, net=pwr_gnd_net.name))
+    return True
+
+# Pair VDD/VPWR and VSS/VGND nets
+#   these commands are already added in Innovus.write_netlist,
+#   but must also occur before power straps are placed
+def sky130_connect_nets2(ht: HammerTool) -> bool:
+    sky130_connect_nets(ht)
     return True
 
 
@@ -307,6 +334,19 @@ def sky130_add_endcaps(ht: HammerTool) -> bool:
     )
     return True
 
+def sky130_set_wire_rc(ht: HammerTool) -> bool:
+    assert isinstance(ht, HammerPlaceAndRouteTool), "set wire rc only for par"
+    assert isinstance(ht, TCLTool), "set wire rc can only run on TCL tools"
+    sky130_plugin_path=ht.get_setting('vlsi.core.technology_path')[-1]
+    ht.append(f"""
+################################################################
+# Repair max slew/cap/fanout violations and normalize slews
+source {sky130_plugin_path}/sky130/extra/sky130hd.rc
+set_wire_rc -signal -layer "met2"
+set_wire_rc -clock  -layer "met5"
+    """)
+    return True
+
 def drc_blackbox_openram_srams(ht: HammerTool) -> bool:
     assert isinstance(ht, HammerDRCTool), "Exlude SRAMs only in DRC"
     drc_box = ''
diff --git a/src/hammer-vlsi/technology/sky130/defaults.yml b/src/hammer-vlsi/technology/sky130/defaults.yml
index 3b69c36df..3c8c8f15f 100644
--- a/src/hammer-vlsi/technology/sky130/defaults.yml
+++ b/src/hammer-vlsi/technology/sky130/defaults.yml
@@ -90,9 +90,42 @@ technology.core:
   std_cell_rail_layer: "met1" 
   # This is used to provide a reference master for generating standard cells
   tap_cell_rail_reference: "sky130_fd_sc_hd__tap*"
+  # default power pins are VDD/VSS
+  std_cell_supplies: 
+    power:  ["VPWR"]
+    ground: ["VGND"]
 
 par.inputs:
   gds_merge: true
   gds_map_mode: manual
   gds_map_file: "${vlsi.builtins.hammer_vlsi_path}/technology/sky130/extra/sky130_lefpin.map"
   gds_map_file_meta: lazysubst
+  openrcx_techfile: "${vlsi.builtins.hammer_vlsi_path}/technology/sky130/extra/sky130hd.rcx_rules"
+  openrcx_techfile_meta: lazysubst
+  klayout_techfile_source: "${technology.sky130.sky130A}/libs.tech/klayout/sky130A.lyt"
+  klayout_techfile_source_meta: lazysubst
+
+# Power Straps
+par.power_straps_mode: generate
+par.generate_power_straps_method: by_tracks
+par.blockage_spacing: 2.0
+par.blockage_spacing_top_layer: met4
+par.generate_power_straps_options:
+  by_tracks:
+    strap_layers:
+      - met2
+      - met3
+      - met4
+      - met5
+    pin_layers:
+      - met5
+    blockage_spacing_met2: 4.0
+    track_width: 6
+    track_width_met5: 2
+    track_spacing: 1
+    track_start: 10
+    track_start_met5: 1
+    power_utilization: 0.1
+    power_utilization_met2: 0.05
+    power_utilization_met4: 0.15
+    power_utilization_met5: 0.5
diff --git a/src/hammer-vlsi/technology/sky130/extra/def2stream.py b/src/hammer-vlsi/technology/sky130/extra/def2stream.py
new file mode 100644
index 000000000..76c9ca10f
--- /dev/null
+++ b/src/hammer-vlsi/technology/sky130/extra/def2stream.py
@@ -0,0 +1,191 @@
+import pya
+import re
+import json
+import copy
+import sys
+import os
+
+errors = 0
+
+# Expand layers in json
+def expand_cfg_layers(cfg):
+  layers = cfg['layers']
+  expand = [layer for layer in layers if 'layers' in layers[layer]]
+  for layer in expand:
+    for i, (name, num) in enumerate(zip(layers[layer]['names'],
+                                        layers[layer]['layers'])):
+      new_layer = copy.deepcopy(layers[layer])
+      del new_layer['names']
+      new_layer['name'] = name
+      del new_layer['layers']
+      new_layer['layer'] = num
+      layers[name] = new_layer
+    del layers[layer]
+
+def read_cfg():
+  print('INFO: Reading config file: ' + config_file)
+  with open(config_file, 'r') as f:
+    cfg = json.load(f)
+
+  expand_cfg_layers(cfg)
+  cfg = cfg['layers'] # ignore the rest
+
+  # Map gds layers & datatype to KLayout indices
+  # These are arrays for the different mask numbers
+  for layer, vals in cfg.items():
+    layer = vals['layer']
+    for key in ('opc', 'non-opc'):
+      if key not in vals:
+        continue
+      data = vals[key]
+      if isinstance(data['datatype'], int):
+        data['datatype'] = [data['datatype']] # convert to array
+      data['klayout'] = [main_layout.find_layer(layer, datatype)
+                         for datatype in data['datatype']]
+
+  return cfg
+
+#match a line like:
+# - LAYER M2 + MASK 2 + OPC RECT ( 3000 3000 ) ( 5000 5000 ) ;
+rect_pat = re.compile(r'''
+  \s*\-\ LAYER\ (?P<layer>\S+)  # The layer name
+  (?:                           # Non-capturing group
+  \s+\+\ MASK\ (?P<mask>\d+)    # Mask, None if absent
+  )?
+  (?P<opc>                      # OPC, None if absent
+  \s+\+\ OPC
+  )?
+  \s+RECT\
+   \(\ (?P<xlo>\d+)\ (?P<ylo>\d+)\ \)\   # rect lower-left pt
+  \(\ (?P<xhi>\d+)\ (?P<yhi>\d+)\ \)\ ; # rect upper-right pt
+  ''',
+                      re.VERBOSE)
+
+def read_fills(top):
+  if config_file == '':
+    print('WARNING: no fill config file specified')
+    return
+  # KLayout doesn't support FILL in DEF so we have to side load them :(
+  cfg = read_cfg()
+  in_fills = False
+  units = None
+  with open(in_def) as fp:
+    for line in fp:
+      if in_fills:
+        if re.match('END FILLS', line):
+          break # done with fills; don't care what follows
+        m = re.match(rect_pat, line)
+        if not m:
+          raise Exception('Unrecognized fill: ' + line)
+        opc_type = 'opc' if m.group('opc') else 'non-opc'
+        mask = m.group('mask')
+        if not mask: #uncolored just uses first entry
+          mask = 0
+        else:
+          mask = int(mask) - 1 # DEF is 1-based indexing
+        layer = cfg[m.group('layer')][opc_type]['klayout'][mask]
+        xlo = int(m.group('xlo')) / units
+        ylo = int(m.group('ylo')) / units
+        xhi = int(m.group('xhi')) / units
+        yhi = int(m.group('yhi')) / units
+        top.shapes(layer).insert(pya.DBox(xlo, ylo, xhi, yhi))
+      elif re.match('FILLS \d+ ;', line):
+        in_fills = True
+      elif not units:
+        m = re.match('UNITS DISTANCE MICRONS (\d+)', line)
+        if m:
+          units = float(m.group(1))
+
+# Load technology file
+tech = pya.Technology()
+tech.load(tech_file)
+layoutOptions = tech.load_layout_options
+
+# Load def file
+main_layout = pya.Layout()
+print("[INFO] Reporting cells prior to loading DEF ...")
+for i in main_layout.each_cell():
+  print("[INFO] '{0}'".format(i.name))
+
+print("[INFO] Reading DEF ...")
+main_layout.read(in_def, layoutOptions)
+
+#print("[INFO] Reporting cells after loading DEF ...")
+#for i in main_layout.each_cell():
+#  print("[INFO] '{0}'".format(i.name))
+
+# Clear cells
+top_cell_index = main_layout.cell(design_name).cell_index()
+
+# remove orphan cell BUT preserve cell with VIA_
+#  - KLayout is prepending VIA_ when reading DEF that instantiates LEF's via
+print("[INFO] Clearing cells...")
+for i in main_layout.each_cell():
+  if i.cell_index() != top_cell_index:
+    if not i.name.startswith("VIA_"):
+      i.clear()
+
+# Load in the gds to merge
+print("[INFO] Merging GDS/OAS files...")
+for fil in in_files.split():
+  print("\t{0}".format(fil))
+  main_layout.read(fil)
+
+# Copy the top level only to a new layout
+print("[INFO] Copying toplevel cell '{0}'".format(design_name))
+top_only_layout = pya.Layout()
+top_only_layout.dbu = main_layout.dbu
+top = top_only_layout.create_cell(design_name)
+top.copy_tree(main_layout.cell(design_name))
+
+read_fills(top)
+
+print("[INFO] Checking for missing cell from GDS/OAS...")
+missing_cell = False
+regex = None
+if 'GDS_ALLOW_EMPTY' in os.environ:
+    print("[INFO] Found GDS_ALLOW_EMPTY variable.")
+    regex = os.getenv('GDS_ALLOW_EMPTY')
+for i in top_only_layout.each_cell():
+  if i.is_empty():
+    missing_cell = True
+    if regex is not None and re.match(regex, i.name):
+        print("[WARNING] LEF Cell '{0}' ignored. Matches GDS_ALLOW_EMPTY.".format(i.name))
+    else:
+        print("[ERROR] LEF Cell '{0}' has no matching GDS/OAS cell."
+              " Cell will be empty.".format(i.name))
+        errors += 1
+
+if not missing_cell:
+  print("[INFO] All LEF cells have matching GDS/OAS cells")
+
+print("[INFO] Checking for orphan cell in the final layout...")
+orphan_cell = False
+for i in top_only_layout.each_cell():
+  if i.name != design_name and i.parent_cells() == 0:
+    orphan_cell = True
+    print("[ERROR] Found orphan cell '{0}'".format(i.name))
+    errors += 1
+
+if not orphan_cell:
+  print("[INFO] No orphan cells")
+
+
+if seal_file:
+
+  top_cell = top_only_layout.top_cell()
+
+  print("[INFO] Reading seal GDS/OAS file...")
+  print("\t{0}".format(seal_file))
+  top_only_layout.read(seal_file)
+
+  for cell in top_only_layout.top_cells():
+    if cell != top_cell:
+      print("[INFO] Merging '{0}' as child of '{1}'".format(cell.name, top_cell.name))
+      top.insert(pya.CellInstArray(cell.cell_index(), pya.Trans()))
+
+# Write out the GDS
+print("[INFO] Writing out GDS/OAS '{0}'".format(out_file))
+top_only_layout.write(out_file)
+
+sys.exit(errors)
diff --git a/src/hammer-vlsi/technology/sky130/extra/fill.json b/src/hammer-vlsi/technology/sky130/extra/fill.json
new file mode 100644
index 000000000..3efce0109
--- /dev/null
+++ b/src/hammer-vlsi/technology/sky130/extra/fill.json
@@ -0,0 +1,83 @@
+{
+    "layers" : {
+        "met1" : {
+            "layer":                   36,
+            "name":                    "met1",
+            "dir":                     "H",
+            "datatype":                 0,
+            "space_to_outline":         70,
+            "non-opc": {
+                "datatype":             0,
+                "width":                [2.00, 1.00, 0.58, 0.3],
+                "height":               [2.00, 1.00, 0.58, 0.3],
+                "space_to_fill":        0.3,
+                "space_to_non_fill":    3
+            }
+        },
+        
+        "met2" : {
+            "layer":                   41,
+            "name":                    "met2",
+            "dir":                     "V",
+            "datatype":                 0,
+            "space_to_outline":         70,
+            "non-opc": {
+                "datatype":             0,
+                "width":                [2.00, 1.00, 0.58, 0.3],
+                "height":               [2.00, 1.00, 0.58, 0.3],
+                "space_to_fill":        0.3,
+                "space_to_non_fill":    3
+            }
+        },
+
+        "met3" : {
+            "layer":                   34,
+            "name":                    "met3",
+            "dir":                     "H",
+            "datatype":                 0,
+            "space_to_outline":         70,
+            "non-opc": {
+                "datatype":             0,
+                "width":                [2.00, 1.00, 0.58, 0.4],
+                "height":               [2.00, 1.00, 0.58, 0.4],
+                "space_to_fill":        0.3,
+                "space_to_non_fill":    3
+            }
+        },
+        
+        "met4" : {
+            "layer":                   51,
+            "name":                    "met4",
+            "dir":                     "V",
+            "datatype":                 0,
+            "space_to_outline":         70,
+            "non-opc": {
+                "datatype":             0,
+                "width":                [2.00, 1.00, 0.58, 0.4],
+                "height":               [2.00, 1.00, 0.58, 0.4],
+                "space_to_fill":        0.3,
+                "space_to_non_fill":    3.3
+            }
+        },
+
+        "met5" : {
+            "layer":                   59,
+            "name":                    "met5",
+            "dir":                     "H",
+            "datatype":                 0,
+            "space_to_outline":         70,
+            "non-opc": {
+                "datatype":             0,
+                "width":                [2.00, 1.00, 0.58, 0.4],
+                "height":               [2.00, 1.00, 0.58, 0.4],
+                "space_to_fill":        0.3,
+                "space_to_non_fill":    3.3
+            }
+        }
+    },
+    "outline" : {
+        "layer":                     237,
+        "datatype":                  0
+    }
+}
+
diff --git a/src/hammer-vlsi/technology/sky130/extra/sky130hd.rc b/src/hammer-vlsi/technology/sky130/extra/sky130hd.rc
new file mode 100644
index 000000000..09afb585f
--- /dev/null
+++ b/src/hammer-vlsi/technology/sky130/extra/sky130hd.rc
@@ -0,0 +1,13 @@
+# find_wire_rc -38% adjustment
+set_layer_rc -layer li1 -resistance 6.829e-02 -capacitance 1.265e-04
+set_layer_rc -layer met1 -resistance 1.207e-03 -capacitance 1.389e-04
+set_layer_rc -layer met2 -resistance 1.223e-03 -capacitance 1.388e-04
+set_layer_rc -layer met3 -resistance 1.668e-04 -capacitance 1.406e-04
+set_layer_rc -layer met4 -resistance 1.686e-04 -capacitance 1.364e-04
+set_layer_rc -layer met5 -resistance 1.865e-05 -capacitance 1.389e-04
+
+set_layer_rc -via mcon -resistance 9.249146E-3
+set_layer_rc -via via -resistance 4.5E-3
+set_layer_rc -via via2 -resistance 3.368786E-3
+set_layer_rc -via via3 -resistance 0.376635E-3
+set_layer_rc -via via4 -resistance 0.00580E-3
\ No newline at end of file
diff --git a/src/hammer-vlsi/technology/sky130/extra/sky130hd.rcx_rules b/src/hammer-vlsi/technology/sky130/extra/sky130hd.rcx_rules
new file mode 100644
index 000000000..af5cfffd6
--- /dev/null
+++ b/src/hammer-vlsi/technology/sky130/extra/sky130hd.rcx_rules
@@ -0,0 +1,1645 @@
+Extraction Rules for OpenRCX
+
+DIAGMODEL ON
+
+LayerCount 6
+DensityRate 1  0
+
+DensityModel 0
+
+Metal 1 RESOVER
+WIDTH Table 1 entries:  0.17
+
+Metal 1 RESOVER 0
+DIST count 55 width 0.17
+0 0 0 0.0681705
+0 0.31 0 0.0681705
+0 0.48 0 0.0681705
+0 0.705 0 0.0681705
+0 0.79 0 0.0681705
+0 0.96 0 0.0681705
+0 1.185 0 0.0681705
+0 1.27 0 0.0681705
+0 1.44 0 0.0681705
+0 1.92 0 0.0681705
+0.31 0.31 0 0.0681705
+0.31 0.48 0 0.0681705
+0.31 0.705 0 0.0681705
+0.31 0.79 0 0.0681705
+0.31 0.96 0 0.0681705
+0.31 1.185 0 0.0681705
+0.31 1.27 0 0.0681705
+0.31 1.44 0 0.0681705
+0.31 1.92 0 0.0681705
+0.48 0.48 0 0.0681705
+0.48 0.705 0 0.0681705
+0.48 0.79 0 0.0681705
+0.48 0.96 0 0.0681705
+0.48 1.185 0 0.0681705
+0.48 1.27 0 0.0681705
+0.48 1.44 0 0.0681705
+0.48 1.92 0 0.0681705
+0.705 0.705 0 0.0681705
+0.705 0.79 0 0.0681705
+0.705 0.96 0 0.0681705
+0.705 1.185 0 0.0681705
+0.705 1.27 0 0.0681705
+0.705 1.44 0 0.0681705
+0.705 1.92 0 0.0681705
+0.79 0.79 0 0.0681705
+0.79 0.96 0 0.0681705
+0.79 1.185 0 0.0681705
+0.79 1.27 0 0.0681705
+0.79 1.44 0 0.0681705
+0.79 1.92 0 0.0681705
+0.96 0.96 0 0.0681705
+0.96 1.185 0 0.0681705
+0.96 1.27 0 0.0681705
+0.96 1.44 0 0.0681705
+0.96 1.92 0 0.0681705
+1.185 1.185 0 0.0681705
+1.185 1.27 0 0.0681705
+1.185 1.44 0 0.0681705
+1.185 1.92 0 0.0681705
+1.27 1.27 0 0.0681705
+1.27 1.44 0 0.0681705
+1.27 1.92 0 0.0681705
+1.44 1.44 0 0.0681705
+1.44 1.92 0 0.0681705
+1.92 0 1.92 0.0681705
+END DIST
+
+Metal 1 OVER
+WIDTH Table 1 entries:  0.17
+
+Metal 1 OVER 0
+DIST count 12 width 0.17
+0.17 8.61372e-05 9.44952e-06 0.0340853
+0.34 5.12844e-05 1.33801e-05 0.0340853
+0.425 4.3541e-05 1.4824e-05 0.0340853
+0.51 3.88428e-05 1.54397e-05 0.0340853
+0.595 3.34495e-05 1.78955e-05 0.0340853
+0.68 3.09507e-05 1.8301e-05 0.0340853
+0.765 2.52695e-05 2.19586e-05 0.0340853
+0.85 2.37271e-05 2.22492e-05 0.0340853
+1.02 2.19149e-05 2.261e-05 0.0340853
+1.36 1.91487e-05 2.24345e-05 0.0340853
+1.7 0 3.91595e-05 0.0340853
+2.04 0 3.91595e-05 0.0340853
+END DIST
+
+Metal 1 UNDER
+WIDTH Table 1 entries:  0.17
+
+Metal 1 UNDER 2
+DIST count 12 width 0.17
+0.17 6.80777e-05 3.34433e-05 0.0340853
+0.34 3.61876e-05 3.33933e-05 0.0340853
+0.425 3.23648e-05 3.24802e-05 0.0340853
+0.51 2.79091e-05 3.41271e-05 0.0340853
+0.595 2.3655e-05 3.64674e-05 0.0340853
+0.68 1.98402e-05 3.89822e-05 0.0340853
+0.765 1.61768e-05 4.11339e-05 0.0340853
+0.85 1.35702e-05 4.31009e-05 0.0340853
+1.02 9.32626e-06 4.69828e-05 0.0340853
+1.36 1.29978e-05 4.73246e-05 0.0340853
+1.7 0 5.96826e-05 0.0340853
+2.04 0 5.96818e-05 0.0340853
+END DIST
+
+Metal 1 UNDER 3
+DIST count 12 width 0.17
+0.17 7.93746e-05 1.67654e-05 0.0340853
+0.34 4.55879e-05 1.9837e-05 0.0340853
+0.425 3.80076e-05 2.15216e-05 0.0340853
+0.51 3.33286e-05 2.23055e-05 0.0340853
+0.595 2.90274e-05 2.37905e-05 0.0340853
+0.68 2.61838e-05 2.46021e-05 0.0340853
+0.765 2.17494e-05 2.71055e-05 0.0340853
+0.85 1.97574e-05 2.80056e-05 0.0340853
+1.02 1.68608e-05 2.99991e-05 0.0340853
+1.36 1.18524e-05 3.36875e-05 0.0340853
+1.7 0 4.32588e-05 0.0340853
+2.04 0 4.32588e-05 0.0340853
+END DIST
+
+Metal 1 UNDER 4
+DIST count 12 width 0.17
+0.17 8.1339e-05 1.43746e-05 0.0340853
+0.34 4.83253e-05 1.64574e-05 0.0340853
+0.425 4.06995e-05 1.81761e-05 0.0340853
+0.51 3.59956e-05 1.90192e-05 0.0340853
+0.595 3.09877e-05 2.11822e-05 0.0340853
+0.68 2.81327e-05 2.18956e-05 0.0340853
+0.765 2.30317e-05 2.48824e-05 0.0340853
+0.85 2.11752e-05 2.54048e-05 0.0340853
+1.02 1.88539e-05 2.62139e-05 0.0340853
+1.36 1.58653e-05 2.65591e-05 0.0340853
+1.7 0 4.12234e-05 0.0340853
+2.04 0 4.12234e-05 0.0340853
+END DIST
+
+Metal 1 UNDER 5
+DIST count 12 width 0.17
+0.17 8.24722e-05 1.31413e-05 0.0340853
+0.34 4.90302e-05 1.57381e-05 0.0340853
+0.425 4.16521e-05 1.71229e-05 0.0340853
+0.51 3.71326e-05 1.76188e-05 0.0340853
+0.595 3.23129e-05 1.94861e-05 0.0340853
+0.68 2.95651e-05 2.00547e-05 0.0340853
+0.765 2.44851e-05 2.3032e-05 0.0340853
+0.85 2.26448e-05 2.35791e-05 0.0340853
+1.02 2.0326e-05 2.45073e-05 0.0340853
+1.36 1.72821e-05 2.52146e-05 0.0340853
+1.7 0 4.02602e-05 0.0340853
+2.04 0 4.02602e-05 0.0340853
+END DIST
+
+Metal 1 UNDER 6
+DIST count 12 width 0.17
+0.17 8.19265e-05 1.35744e-05 0.0340853
+0.34 4.90529e-05 1.5458e-05 0.0340853
+0.425 4.1736e-05 1.67972e-05 0.0340853
+0.51 3.7272e-05 1.72522e-05 0.0340853
+0.595 3.24866e-05 1.90445e-05 0.0340853
+0.68 2.97193e-05 1.95578e-05 0.0340853
+0.765 2.46764e-05 2.24312e-05 0.0340853
+0.85 2.28305e-05 2.29498e-05 0.0340853
+1.02 2.0544e-05 2.39104e-05 0.0340853
+1.36 1.7595e-05 2.51078e-05 0.0340853
+1.7 0 3.97983e-05 0.0340853
+2.04 0 3.97982e-05 0.0340853
+END DIST
+
+Metal 1 DIAGUNDER
+WIDTH Table 1 entries:  0.17
+
+Metal 1 DIAGUNDER 2
+DIST count 13 width 0.17
+0 0 2.66887e-05 0.0340853
+0.17 0 2.24007e-05 0.0340853
+0.34 0 1.9211e-05 0.0340853
+0.425 0 1.79128e-05 0.0340853
+0.51 0 1.68696e-05 0.0340853
+0.595 0 1.60676e-05 0.0340853
+0.68 0 1.54732e-05 0.0340853
+0.765 0 1.50473e-05 0.0340853
+0.85 0 1.47528e-05 0.0340853
+1.02 0 1.44392e-05 0.0340853
+1.36 0 1.43783e-05 0.0340853
+1.7 0 9.51316e-06 0.0340853
+2.04 0 0 0.0340853
+END DIST
+
+Metal 1 DIAGUNDER 3
+DIST count 13 width 0.17
+0 0 1.30121e-05 0.0340853
+0.17 0 1.24717e-05 0.0340853
+0.34 0 1.19979e-05 0.0340853
+0.425 0 1.17403e-05 0.0340853
+0.51 0 1.14754e-05 0.0340853
+0.595 0 1.12057e-05 0.0340853
+0.68 0 1.09324e-05 0.0340853
+0.765 0 1.06556e-05 0.0340853
+0.85 0 1.03755e-05 0.0340853
+1.02 0 9.80591e-06 0.0340853
+1.36 0 1.0251e-05 0.0340853
+1.7 0 9.16462e-06 0.0340853
+2.04 0 8.19109e-06 0.0340853
+END DIST
+
+Metal 1 DIAGUNDER 4
+DIST count 13 width 0.17
+0 0 9.24997e-06 0.0340853
+0.17 0 0 0.0340853
+0.34 0 0 0.0340853
+0.425 0 0 0.0340853
+0.51 0 0 0.0340853
+0.595 0 0 0.0340853
+0.68 0 0 0.0340853
+0.765 0 0 0.0340853
+0.85 0 0 0.0340853
+1.02 0 0 0.0340853
+1.36 0 0 0.0340853
+1.7 0 0 0.0340853
+2.04 0 0 0.0340853
+END DIST
+
+Metal 1 DIAGUNDER 5
+DIST count 13 width 0.17
+0 0 6.03723e-06 0.0340853
+0.17 0 0 0.0340853
+0.34 0 0 0.0340853
+0.425 0 0 0.0340853
+0.51 0 0 0.0340853
+0.595 0 0 0.0340853
+0.68 0 0 0.0340853
+0.765 0 0 0.0340853
+0.85 0 0 0.0340853
+1.02 0 0 0.0340853
+1.36 0 0 0.0340853
+1.7 0 0 0.0340853
+2.04 0 0 0.0340853
+END DIST
+
+Metal 1 DIAGUNDER 6
+DIST count 0 width 0.17
+END DIST
+
+Metal 2 RESOVER
+WIDTH Table 1 entries:  0.14
+
+Metal 2 RESOVER 0
+DIST count 55 width 0.14
+0 0 0 0.00120566
+0 0.23 0 0.00120566
+0 0.37 0 0.00120566
+0 0.53 0 0.00120566
+0 0.6 0 0.00120566
+0 0.74 0 0.00120566
+0 0.9 0 0.00120566
+0 0.97 0 0.00120566
+0 1.11 0 0.00120566
+0 1.48 0 0.00120566
+0.23 0.23 0 0.00120566
+0.23 0.37 0 0.00120566
+0.23 0.53 0 0.00120566
+0.23 0.6 0 0.00120566
+0.23 0.74 0 0.00120566
+0.23 0.9 0 0.00120566
+0.23 0.97 0 0.00120566
+0.23 1.11 0 0.00120566
+0.23 1.48 0 0.00120566
+0.37 0.37 0 0.00120566
+0.37 0.53 0 0.00120566
+0.37 0.6 0 0.00120566
+0.37 0.74 0 0.00120566
+0.37 0.9 0 0.00120566
+0.37 0.97 0 0.00120566
+0.37 1.11 0 0.00120566
+0.37 1.48 0 0.00120566
+0.53 0.53 0 0.00120566
+0.53 0.6 0 0.00120566
+0.53 0.74 0 0.00120566
+0.53 0.9 0 0.00120566
+0.53 0.97 0 0.00120566
+0.53 1.11 0 0.00120566
+0.53 1.48 0 0.00120566
+0.6 0.6 0 0.00120566
+0.6 0.74 0 0.00120566
+0.6 0.9 0 0.00120566
+0.6 0.97 0 0.00120566
+0.6 1.11 0 0.00120566
+0.6 1.48 0 0.00120566
+0.74 0.74 0 0.00120566
+0.74 0.9 0 0.00120566
+0.74 0.97 0 0.00120566
+0.74 1.11 0 0.00120566
+0.74 1.48 0 0.00120566
+0.9 0.9 0 0.00120566
+0.9 0.97 0 0.00120566
+0.9 1.11 0 0.00120566
+0.9 1.48 0 0.00120566
+0.97 0.97 0 0.00120566
+0.97 1.11 0 0.00120566
+0.97 1.48 0 0.00120566
+1.11 1.11 0 0.00120566
+1.11 1.48 0 0.00120566
+1.48 0 1.48 0.00120566
+END DIST
+
+Metal 2 RESOVER 1
+DIST count 0 width 0.14
+END DIST
+
+Metal 2 OVER
+WIDTH Table 1 entries:  0.14
+
+Metal 2 OVER 0
+DIST count 12 width 0.14
+0.14 0.000138118 5.73561e-06 0.000602828
+0.28 8.52281e-05 7.80545e-06 0.000602828
+0.35 7.30176e-05 8.35076e-06 0.000602828
+0.42 6.58219e-05 8.11883e-06 0.000602828
+0.49 5.71535e-05 1.02824e-05 0.000602828
+0.56 5.08842e-05 1.18282e-05 0.000602828
+0.63 4.11549e-05 1.83851e-05 0.000602828
+0.7 3.76403e-05 1.93881e-05 0.000602828
+0.84 3.18492e-05 2.13832e-05 0.000602828
+1.12 2.39134e-05 2.43226e-05 0.000602828
+1.4 1.83959e-05 2.6658e-05 0.000602828
+1.68 0 4.29842e-05 0.000602828
+END DIST
+
+Metal 2 OVER 1
+DIST count 12 width 0.14
+0.14 0.000130654 1.567e-05 0.000602828
+0.28 7.42447e-05 2.18678e-05 0.000602828
+0.35 6.24752e-05 2.35482e-05 0.000602828
+0.42 5.54264e-05 2.41468e-05 0.000602828
+0.49 4.83787e-05 2.59601e-05 0.000602828
+0.56 4.21914e-05 2.84295e-05 0.000602828
+0.63 3.48531e-05 3.32113e-05 0.000602828
+0.7 3.12986e-05 3.48401e-05 0.000602828
+0.84 2.36804e-05 3.98139e-05 0.000602828
+1.12 1.6881e-05 4.38251e-05 0.000602828
+1.4 1.26539e-05 4.67742e-05 0.000602828
+1.68 0 5.8778e-05 0.000602828
+END DIST
+
+Metal 2 UNDER
+WIDTH Table 1 entries:  0.14
+
+Metal 2 UNDER 3
+DIST count 12 width 0.14
+0.14 0.000125315 2.16179e-05 0.000602828
+0.28 6.95395e-05 2.85793e-05 0.000602828
+0.35 5.66731e-05 3.2483e-05 0.000602828
+0.42 4.79433e-05 3.60836e-05 0.000602828
+0.49 4.07063e-05 3.92515e-05 0.000602828
+0.56 3.44506e-05 4.25667e-05 0.000602828
+0.63 2.73011e-05 4.77156e-05 0.000602828
+0.7 2.39016e-05 4.955e-05 0.000602828
+0.84 1.85187e-05 5.2665e-05 0.000602828
+1.12 9.52476e-06 5.89915e-05 0.000602828
+1.4 7.1476e-06 6.0619e-05 0.000602828
+1.68 0 6.7952e-05 0.000602828
+END DIST
+
+Metal 2 UNDER 4
+DIST count 12 width 0.14
+0.14 0.000130705 1.50119e-05 0.000602828
+0.28 7.79806e-05 1.64308e-05 0.000602828
+0.35 6.60048e-05 1.74046e-05 0.000602828
+0.42 5.88336e-05 1.74203e-05 0.000602828
+0.49 5.20059e-05 1.8409e-05 0.000602828
+0.56 4.54686e-05 2.03003e-05 0.000602828
+0.63 3.79594e-05 2.46138e-05 0.000602828
+0.7 3.42363e-05 2.57893e-05 0.000602828
+0.84 2.82732e-05 2.79929e-05 0.000602828
+1.12 2.04041e-05 3.13738e-05 0.000602828
+1.4 1.49853e-05 3.43422e-05 0.000602828
+1.68 0 4.79875e-05 0.000602828
+END DIST
+
+Metal 2 UNDER 5
+DIST count 12 width 0.14
+0.14 0.000132208 1.2287e-05 0.000602828
+0.28 8.20972e-05 1.12027e-05 0.000602828
+0.35 6.98093e-05 1.22795e-05 0.000602828
+0.42 6.24113e-05 1.21279e-05 0.000602828
+0.49 5.46395e-05 1.37539e-05 0.000602828
+0.56 4.82341e-05 1.53582e-05 0.000602828
+0.63 3.98791e-05 2.04812e-05 0.000602828
+0.7 3.64359e-05 2.14036e-05 0.000602828
+0.84 3.09173e-05 2.32351e-05 0.000602828
+1.12 2.32619e-05 2.6199e-05 0.000602828
+1.4 1.74854e-05 2.89191e-05 0.000602828
+1.68 0 4.43916e-05 0.000602828
+END DIST
+
+Metal 2 UNDER 6
+DIST count 12 width 0.14
+0.14 0.000129054 1.46584e-05 0.000602828
+0.28 7.94672e-05 1.26295e-05 0.000602828
+0.35 6.82334e-05 1.28446e-05 0.000602828
+0.42 6.16935e-05 1.22491e-05 0.000602828
+0.49 5.4609e-05 1.35439e-05 0.000602828
+0.56 4.85906e-05 1.49208e-05 0.000602828
+0.63 4.0474e-05 1.9796e-05 0.000602828
+0.7 3.71105e-05 2.05385e-05 0.000602828
+0.84 3.16873e-05 2.20032e-05 0.000602828
+1.12 2.4533e-05 2.42645e-05 0.000602828
+1.4 1.95902e-05 2.6429e-05 0.000602828
+1.68 0 4.43317e-05 0.000602828
+END DIST
+
+Metal 2 DIAGUNDER
+WIDTH Table 1 entries:  0.14
+
+Metal 2 DIAGUNDER 3
+DIST count 13 width 0.14
+0 0 3.31347e-05 0.000602828
+0.14 0 2.73289e-05 0.000602828
+0.28 0 2.33157e-05 0.000602828
+0.35 0 2.14946e-05 0.000602828
+0.42 0 1.99143e-05 0.000602828
+0.49 0 1.8593e-05 0.000602828
+0.56 0 1.71886e-05 0.000602828
+0.63 0 1.63751e-05 0.000602828
+0.7 0 1.57544e-05 0.000602828
+0.84 0 1.49643e-05 0.000602828
+1.12 0 1.45003e-05 0.000602828
+1.4 0 1.4692e-05 0.000602828
+1.68 0 1.49947e-05 0.000602828
+END DIST
+
+Metal 2 DIAGUNDER 4
+DIST count 13 width 0.14
+0 0 1.71233e-05 0.000602828
+0.14 0 1.6158e-05 0.000602828
+0.28 0 1.57688e-05 0.000602828
+0.35 0 1.55375e-05 0.000602828
+0.42 0 1.52888e-05 0.000602828
+0.49 0 1.50272e-05 0.000602828
+0.56 0 1.44808e-05 0.000602828
+0.63 0 1.4219e-05 0.000602828
+0.7 0 1.39606e-05 0.000602828
+0.84 0 1.34715e-05 0.000602828
+1.12 0 1.26613e-05 0.000602828
+1.4 0 1.20702e-05 0.000602828
+1.68 0 1.16482e-05 0.000602828
+END DIST
+
+Metal 2 DIAGUNDER 5
+DIST count 13 width 0.14
+0 0 9.81043e-06 0.000602828
+0.14 0 9.44235e-06 0.000602828
+0.28 0 9.37367e-06 0.000602828
+0.35 0 9.33254e-06 0.000602828
+0.42 0 9.28864e-06 0.000602828
+0.49 0 9.24217e-06 0.000602828
+0.56 0 9.02038e-06 0.000602828
+0.63 0 8.97284e-06 0.000602828
+0.7 0 8.92341e-06 0.000602828
+0.84 0 8.81955e-06 0.000602828
+1.12 0 8.59823e-06 0.000602828
+1.4 0 8.37071e-06 0.000602828
+1.68 0 0 0.000602828
+END DIST
+
+Metal 2 DIAGUNDER 6
+DIST count 13 width 0.14
+0 0 8.57973e-06 0.000602828
+0.14 0 0 0.000602828
+0.28 0 0 0.000602828
+0.35 0 0 0.000602828
+0.42 0 0 0.000602828
+0.49 0 0 0.000602828
+0.56 0 0 0.000602828
+0.63 0 0 0.000602828
+0.7 0 0 0.000602828
+0.84 0 0 0.000602828
+1.12 0 0 0.000602828
+1.4 0 0 0.000602828
+1.68 0 0 0.000602828
+END DIST
+
+Metal 2 OVERUNDER
+WIDTH Table 1 entries:  0.14
+
+Metal 2 OVER 1 UNDER 3
+DIST count 12 width 0.14
+0.14 0.000119372 3.57253e-05 0.000602828
+0.28 6.27954e-05 4.44719e-05 0.000602828
+0.35 4.88178e-05 5.08946e-05 0.000602828
+0.42 3.95403e-05 5.63268e-05 0.000602828
+0.49 3.34018e-05 6.23328e-05 0.000602828
+0.56 2.73394e-05 6.6527e-05 0.000602828
+0.63 2.16318e-05 7.10757e-05 0.000602828
+0.7 1.83906e-05 7.35041e-05 0.000602828
+0.84 1.35103e-05 7.73619e-05 0.000602828
+1.12 8.4057e-06 8.16741e-05 0.000602828
+1.4 6.46386e-06 8.30622e-05 0.000602828
+1.68 0 8.8504e-05 0.000602828
+END DIST
+
+Metal 2 OVER 1 UNDER 4
+DIST count 12 width 0.14
+0.14 0.000124988 2.29842e-05 0.000602828
+0.28 6.90886e-05 2.85542e-05 0.000602828
+0.35 5.76192e-05 3.0258e-05 0.000602828
+0.42 5.06751e-05 3.09869e-05 0.000602828
+0.49 4.45953e-05 3.23341e-05 0.000602828
+0.56 3.82076e-05 3.51534e-05 0.000602828
+0.63 3.19793e-05 3.88739e-05 0.000602828
+0.7 2.82205e-05 4.07125e-05 0.000602828
+0.84 2.02291e-05 4.60782e-05 0.000602828
+1.12 1.35021e-05 5.02313e-05 0.000602828
+1.4 9.5176e-06 5.33196e-05 0.000602828
+1.68 0 6.26264e-05 0.000602828
+END DIST
+
+Metal 2 OVER 1 UNDER 5
+DIST count 12 width 0.14
+0.14 0.000126034 2.05039e-05 0.000602828
+0.28 7.19999e-05 2.42552e-05 0.000602828
+0.35 6.00408e-05 2.63318e-05 0.000602828
+0.42 5.27087e-05 2.71038e-05 0.000602828
+0.49 4.60601e-05 2.87162e-05 0.000602828
+0.56 3.97624e-05 3.12743e-05 0.000602828
+0.63 3.32514e-05 3.52182e-05 0.000602828
+0.7 2.9778e-05 3.67814e-05 0.000602828
+0.84 2.22918e-05 4.17105e-05 0.000602828
+1.12 1.59869e-05 4.53682e-05 0.000602828
+1.4 1.17684e-05 4.82969e-05 0.000602828
+1.68 0 5.93339e-05 0.000602828
+END DIST
+
+Metal 2 OVER 1 UNDER 6
+DIST count 12 width 0.14
+0.14 0.000124315 2.20393e-05 0.000602828
+0.28 7.07847e-05 2.48307e-05 0.000602828
+0.35 5.91661e-05 2.66924e-05 0.000602828
+0.42 5.21487e-05 2.73862e-05 0.000602828
+0.49 4.57906e-05 2.8894e-05 0.000602828
+0.56 3.97363e-05 3.12372e-05 0.000602828
+0.63 3.33777e-05 3.49493e-05 0.000602828
+0.7 3.0004e-05 3.62833e-05 0.000602828
+0.84 2.26829e-05 4.077e-05 0.000602828
+1.12 1.67524e-05 4.38231e-05 0.000602828
+1.4 1.29366e-05 4.64806e-05 0.000602828
+1.68 0 5.88991e-05 0.000602828
+END DIST
+
+Metal 3 RESOVER
+WIDTH Table 1 entries:  0.14
+
+Metal 3 RESOVER 0
+DIST count 55 width 0.14
+0 0 0 0.00122133
+0 0.34 0 0.00122133
+0 0.48 0 0.00122133
+0 0.75 0 0.00122133
+0 0.82 0 0.00122133
+0 0.96 0 0.00122133
+0 1.23 0 0.00122133
+0 1.3 0 0.00122133
+0 1.44 0 0.00122133
+0 1.92 0 0.00122133
+0.34 0.34 0 0.00122133
+0.34 0.48 0 0.00122133
+0.34 0.75 0 0.00122133
+0.34 0.82 0 0.00122133
+0.34 0.96 0 0.00122133
+0.34 1.23 0 0.00122133
+0.34 1.3 0 0.00122133
+0.34 1.44 0 0.00122133
+0.34 1.92 0 0.00122133
+0.48 0.48 0 0.00122133
+0.48 0.75 0 0.00122133
+0.48 0.82 0 0.00122133
+0.48 0.96 0 0.00122133
+0.48 1.23 0 0.00122133
+0.48 1.3 0 0.00122133
+0.48 1.44 0 0.00122133
+0.48 1.92 0 0.00122133
+0.75 0.75 0 0.00122133
+0.75 0.82 0 0.00122133
+0.75 0.96 0 0.00122133
+0.75 1.23 0 0.00122133
+0.75 1.3 0 0.00122133
+0.75 1.44 0 0.00122133
+0.75 1.92 0 0.00122133
+0.82 0.82 0 0.00122133
+0.82 0.96 0 0.00122133
+0.82 1.23 0 0.00122133
+0.82 1.3 0 0.00122133
+0.82 1.44 0 0.00122133
+0.82 1.92 0 0.00122133
+0.96 0.96 0 0.00122133
+0.96 1.23 0 0.00122133
+0.96 1.3 0 0.00122133
+0.96 1.44 0 0.00122133
+0.96 1.92 0 0.00122133
+1.23 1.23 0 0.00122133
+1.23 1.3 0 0.00122133
+1.23 1.44 0 0.00122133
+1.23 1.92 0 0.00122133
+1.3 1.3 0 0.00122133
+1.3 1.44 0 0.00122133
+1.3 1.92 0 0.00122133
+1.44 1.44 0 0.00122133
+1.44 1.92 0 0.00122133
+1.92 0 1.92 0.00122133
+END DIST
+
+Metal 3 RESOVER 1
+DIST count 0 width 0.14
+END DIST
+
+Metal 3 RESOVER 2
+DIST count 0 width 0.14
+END DIST
+
+Metal 3 OVER
+WIDTH Table 1 entries:  0.14
+
+Metal 3 OVER 0
+DIST count 12 width 0.14
+0.14 0.000136816 6.4904e-06 0.000610664
+0.28 8.34141e-05 9.07691e-06 0.000610664
+0.35 7.14996e-05 9.78972e-06 0.000610664
+0.42 6.58888e-05 8.30119e-06 0.000610664
+0.49 5.83556e-05 9.50429e-06 0.000610664
+0.56 4.70158e-05 1.65661e-05 0.000610664
+0.63 4.32143e-05 1.7029e-05 0.000610664
+0.7 4.01096e-05 1.74903e-05 0.000610664
+0.84 3.52495e-05 1.84518e-05 0.000610664
+1.12 2.83e-05 2.04259e-05 0.000610664
+1.4 2.35213e-05 2.18521e-05 0.000610664
+1.68 0 4.29564e-05 0.000610664
+END DIST
+
+Metal 3 OVER 1
+DIST count 12 width 0.14
+0.14 0.000127192 1.69995e-05 0.000610664
+0.28 7.66247e-05 1.63418e-05 0.000610664
+0.35 6.50566e-05 1.70294e-05 0.000610664
+0.42 5.90549e-05 1.62173e-05 0.000610664
+0.49 5.32273e-05 1.65602e-05 0.000610664
+0.56 4.46891e-05 2.114e-05 0.000610664
+0.63 4.07293e-05 2.19278e-05 0.000610664
+0.7 3.74585e-05 2.26078e-05 0.000610664
+0.84 3.23579e-05 2.37641e-05 0.000610664
+1.12 2.54578e-05 2.5716e-05 0.000610664
+1.4 2.10804e-05 2.71969e-05 0.000610664
+1.68 0 4.64982e-05 0.000610664
+END DIST
+
+Metal 3 OVER 2
+DIST count 12 width 0.14
+0.14 0.000124423 2.32963e-05 0.000610664
+0.28 6.96191e-05 2.85508e-05 0.000610664
+0.35 5.56138e-05 3.34378e-05 0.000610664
+0.42 4.92953e-05 3.40191e-05 0.000610664
+0.49 4.27985e-05 3.56844e-05 0.000610664
+0.56 3.40381e-05 4.10225e-05 0.000610664
+0.63 2.98499e-05 4.25768e-05 0.000610664
+0.7 2.64613e-05 4.39629e-05 0.000610664
+0.84 2.01626e-05 4.76138e-05 0.000610664
+1.12 1.40572e-05 5.12535e-05 0.000610664
+1.4 1.08465e-05 5.32306e-05 0.000610664
+1.68 0 6.30477e-05 0.000610664
+END DIST
+
+Metal 3 UNDER
+WIDTH Table 1 entries:  0.14
+
+Metal 3 UNDER 4
+DIST count 12 width 0.14
+0.14 0.000126123 1.95052e-05 0.000610664
+0.28 7.26959e-05 2.23848e-05 0.000610664
+0.35 5.95301e-05 2.56742e-05 0.000610664
+0.42 5.08436e-05 2.8336e-05 0.000610664
+0.49 4.43139e-05 3.00085e-05 0.000610664
+0.56 3.54721e-05 3.54467e-05 0.000610664
+0.63 3.14061e-05 3.68564e-05 0.000610664
+0.7 2.81522e-05 3.8015e-05 0.000610664
+0.84 2.33034e-05 3.98469e-05 0.000610664
+1.12 1.70668e-05 4.26181e-05 0.000610664
+1.4 1.30512e-05 4.46023e-05 0.000610664
+1.68 0 5.62342e-05 0.000610664
+END DIST
+
+Metal 3 UNDER 5
+DIST count 12 width 0.14
+0.14 0.000128808 1.51914e-05 0.000610664
+0.28 7.72605e-05 1.52063e-05 0.000610664
+0.35 6.57281e-05 1.59295e-05 0.000610664
+0.42 5.97961e-05 1.5065e-05 0.000610664
+0.49 5.40428e-05 1.52938e-05 0.000610664
+0.56 4.55078e-05 1.98013e-05 0.000610664
+0.63 4.15542e-05 2.05024e-05 0.000610664
+0.7 3.82674e-05 2.11168e-05 0.000610664
+0.84 3.30494e-05 2.22271e-05 0.000610664
+1.12 2.56053e-05 2.43909e-05 0.000610664
+1.4 2.05985e-05 2.61987e-05 0.000610664
+1.68 0 4.48263e-05 0.000610664
+END DIST
+
+Metal 3 UNDER 6
+DIST count 12 width 0.14
+0.14 0.000131355 1.24995e-05 0.000610664
+0.28 8.05674e-05 1.16327e-05 0.000610664
+0.35 6.90668e-05 1.23992e-05 0.000610664
+0.42 6.29339e-05 1.15131e-05 0.000610664
+0.49 5.61372e-05 1.25383e-05 0.000610664
+0.56 4.63299e-05 1.80852e-05 0.000610664
+0.63 4.22832e-05 1.87244e-05 0.000610664
+0.7 3.8944e-05 1.93101e-05 0.000610664
+0.84 3.37351e-05 2.04174e-05 0.000610664
+1.12 2.65995e-05 2.25418e-05 0.000610664
+1.4 2.1936e-05 2.41638e-05 0.000610664
+1.68 0 4.40076e-05 0.000610664
+END DIST
+
+Metal 3 DIAGUNDER
+WIDTH Table 1 entries:  0.14
+
+Metal 3 DIAGUNDER 4
+DIST count 13 width 0.14
+0 0 2.89764e-05 0.000610664
+0.14 0 2.70012e-05 0.000610664
+0.28 0 2.51016e-05 0.000610664
+0.35 0 2.40922e-05 0.000610664
+0.42 0 2.30957e-05 0.000610664
+0.49 0 2.21433e-05 0.000610664
+0.56 0 2.12575e-05 0.000610664
+0.63 0 2.04523e-05 0.000610664
+0.7 0 1.97342e-05 0.000610664
+0.84 0 1.85571e-05 0.000610664
+1.12 0 1.70525e-05 0.000610664
+1.4 0 1.62383e-05 0.000610664
+1.68 0 1.57202e-05 0.000610664
+END DIST
+
+Metal 3 DIAGUNDER 5
+DIST count 13 width 0.14
+0 0 1.23209e-05 0.000610664
+0.14 0 1.19729e-05 0.000610664
+0.28 0 1.18364e-05 0.000610664
+0.35 0 1.1765e-05 0.000610664
+0.42 0 1.1692e-05 0.000610664
+0.49 0 1.16174e-05 0.000610664
+0.56 0 1.15415e-05 0.000610664
+0.63 0 1.14645e-05 0.000610664
+0.7 0 1.13866e-05 0.000610664
+0.84 0 1.12286e-05 0.000610664
+1.12 0 1.09062e-05 0.000610664
+1.4 0 1.05776e-05 0.000610664
+1.68 0 1.02431e-05 0.000610664
+END DIST
+
+Metal 3 DIAGUNDER 6
+DIST count 13 width 0.14
+0 0 1.0436e-05 0.000610664
+0.14 0 1.03181e-05 0.000610664
+0.28 0 1.02827e-05 0.000610664
+0.35 0 1.02641e-05 0.000610664
+0.42 0 1.0245e-05 0.000610664
+0.49 0 1.02253e-05 0.000610664
+0.56 0 1.0205e-05 0.000610664
+0.63 0 1.01841e-05 0.000610664
+0.7 0 1.01626e-05 0.000610664
+0.84 0 1.01175e-05 0.000610664
+1.12 0 1.00172e-05 0.000610664
+1.4 0 9.8733e-06 0.000610664
+1.68 0 9.58175e-06 0.000610664
+END DIST
+
+Metal 3 OVERUNDER
+WIDTH Table 1 entries:  0.14
+
+Metal 3 OVER 1 UNDER 4
+DIST count 12 width 0.14
+0.14 0.000121272 2.58426e-05 0.000610664
+0.28 6.86112e-05 2.78188e-05 0.000610664
+0.35 5.54947e-05 3.12117e-05 0.000610664
+0.42 4.65322e-05 3.42706e-05 0.000610664
+0.49 4.08755e-05 3.55205e-05 0.000610664
+0.56 3.35405e-05 3.96486e-05 0.000610664
+0.63 2.94009e-05 4.12735e-05 0.000610664
+0.7 2.60535e-05 4.26151e-05 0.000610664
+0.84 2.10025e-05 4.47461e-05 0.000610664
+1.12 1.4424e-05 4.80529e-05 0.000610664
+1.4 1.03605e-05 5.03658e-05 0.000610664
+1.68 0 5.95973e-05 0.000610664
+END DIST
+
+Metal 3 OVER 1 UNDER 5
+DIST count 12 width 0.14
+0.14 0.000123453 2.16163e-05 0.000610664
+0.28 7.35216e-05 1.98995e-05 0.000610664
+0.35 6.18244e-05 2.10267e-05 0.000610664
+0.42 5.53367e-05 2.0826e-05 0.000610664
+0.49 5.02035e-05 2.08249e-05 0.000610664
+0.56 4.30336e-05 2.41143e-05 0.000610664
+0.63 3.89508e-05 2.5055e-05 0.000610664
+0.7 3.55934e-05 2.58272e-05 0.000610664
+0.84 3.04001e-05 2.70796e-05 0.000610664
+1.12 2.34671e-05 2.92113e-05 0.000610664
+1.4 1.90797e-05 3.10369e-05 0.000610664
+1.68 0 4.87429e-05 0.000610664
+END DIST
+
+Metal 3 OVER 1 UNDER 6
+DIST count 12 width 0.14
+0.14 0.000124949 1.96143e-05 0.000610664
+0.28 7.52264e-05 1.76242e-05 0.000610664
+0.35 6.37136e-05 1.85306e-05 0.000610664
+0.42 5.72626e-05 1.81979e-05 0.000610664
+0.49 5.17016e-05 1.85489e-05 0.000610664
+0.56 4.39077e-05 2.23637e-05 0.000610664
+0.63 3.9734e-05 2.33058e-05 0.000610664
+0.7 3.62877e-05 2.4091e-05 0.000610664
+0.84 3.09712e-05 2.53663e-05 0.000610664
+1.12 2.40379e-05 2.745e-05 0.000610664
+1.4 1.97744e-05 2.91518e-05 0.000610664
+1.68 0 4.74739e-05 0.000610664
+END DIST
+
+Metal 3 OVER 2 UNDER 4
+DIST count 12 width 0.14
+0.14 0.000115608 3.50312e-05 0.000610664
+0.28 5.83119e-05 4.55418e-05 0.000610664
+0.35 4.49743e-05 5.09966e-05 0.000610664
+0.42 3.77431e-05 5.36392e-05 0.000610664
+0.49 3.17863e-05 5.8277e-05 0.000610664
+0.56 2.4851e-05 6.28011e-05 0.000610664
+0.63 2.09385e-05 6.49097e-05 0.000610664
+0.7 1.79023e-05 6.66157e-05 0.000610664
+0.84 1.3077e-05 6.97887e-05 0.000610664
+1.12 8.49625e-06 7.31327e-05 0.000610664
+1.4 6.21704e-06 7.4809e-05 0.000610664
+1.68 0 8.01802e-05 0.000610664
+END DIST
+
+Metal 3 OVER 2 UNDER 5
+DIST count 12 width 0.14
+0.14 0.000119812 2.94677e-05 0.000610664
+0.28 6.54912e-05 3.40758e-05 0.000610664
+0.35 5.13057e-05 3.94727e-05 0.000610664
+0.42 4.45564e-05 4.07913e-05 0.000610664
+0.49 3.88127e-05 4.2283e-05 0.000610664
+0.56 3.15132e-05 4.63845e-05 0.000610664
+0.63 2.72338e-05 4.81364e-05 0.000610664
+0.7 2.37708e-05 4.96133e-05 0.000610664
+0.84 1.73816e-05 5.3278e-05 0.000610664
+1.12 1.12978e-05 5.68197e-05 0.000610664
+1.4 8.22247e-06 5.88459e-05 0.000610664
+1.68 0 6.6398e-05 0.000610664
+END DIST
+
+Metal 3 OVER 2 UNDER 6
+DIST count 12 width 0.14
+0.14 0.000121356 2.66641e-05 0.000610664
+0.28 6.77499e-05 3.03143e-05 0.000610664
+0.35 5.37608e-05 3.54326e-05 0.000610664
+0.42 4.70926e-05 3.64729e-05 0.000610664
+0.49 4.08946e-05 3.81779e-05 0.000610664
+0.56 3.29078e-05 4.27905e-05 0.000610664
+0.63 2.85927e-05 4.44477e-05 0.000610664
+0.7 2.51025e-05 4.58613e-05 0.000610664
+0.84 1.86949e-05 4.94652e-05 0.000610664
+1.12 1.26703e-05 5.29469e-05 0.000610664
+1.4 9.66659e-06 5.48745e-05 0.000610664
+1.68 0 6.37319e-05 0.000610664
+END DIST
+
+Metal 4 RESOVER
+WIDTH Table 1 entries:  0.3
+
+Metal 4 RESOVER 0
+DIST count 55 width 0.3
+0 0 0 0.000166283
+0 0.44 0 0.000166283
+0 0.74 0 0.000166283
+0 1.03 0 0.000166283
+0 1.18 0 0.000166283
+0 1.48 0 0.000166283
+0 1.77 0 0.000166283
+0 1.92 0 0.000166283
+0 2.22 0 0.000166283
+0 2.96 0 0.000166283
+0.44 0.44 0 0.000166283
+0.44 0.74 0 0.000166283
+0.44 1.03 0 0.000166283
+0.44 1.18 0 0.000166283
+0.44 1.48 0 0.000166283
+0.44 1.77 0 0.000166283
+0.44 1.92 0 0.000166283
+0.44 2.22 0 0.000166283
+0.44 2.96 0 0.000166283
+0.74 0.74 0 0.000166283
+0.74 1.03 0 0.000166283
+0.74 1.18 0 0.000166283
+0.74 1.48 0 0.000166283
+0.74 1.77 0 0.000166283
+0.74 1.92 0 0.000166283
+0.74 2.22 0 0.000166283
+0.74 2.96 0 0.000166283
+1.03 1.03 0 0.000166283
+1.03 1.18 0 0.000166283
+1.03 1.48 0 0.000166283
+1.03 1.77 0 0.000166283
+1.03 1.92 0 0.000166283
+1.03 2.22 0 0.000166283
+1.03 2.96 0 0.000166283
+1.18 1.18 0 0.000166283
+1.18 1.48 0 0.000166283
+1.18 1.77 0 0.000166283
+1.18 1.92 0 0.000166283
+1.18 2.22 0 0.000166283
+1.18 2.96 0 0.000166283
+1.48 1.48 0 0.000166283
+1.48 1.77 0 0.000166283
+1.48 1.92 0 0.000166283
+1.48 2.22 0 0.000166283
+1.48 2.96 0 0.000166283
+1.77 1.77 0 0.000166283
+1.77 1.92 0 0.000166283
+1.77 2.22 0 0.000166283
+1.77 2.96 0 0.000166283
+1.92 1.92 0 0.000166283
+1.92 2.22 0 0.000166283
+1.92 2.96 0 0.000166283
+2.22 2.22 0 0.000166283
+2.22 2.96 0 0.000166283
+2.96 0 2.96 0.000166283
+END DIST
+
+Metal 4 RESOVER 1
+DIST count 0 width 0.3
+END DIST
+
+Metal 4 RESOVER 2
+DIST count 0 width 0.3
+END DIST
+
+Metal 4 RESOVER 3
+DIST count 0 width 0.3
+END DIST
+
+Metal 4 OVER
+WIDTH Table 1 entries:  0.3
+
+Metal 4 OVER 0
+DIST count 12 width 0.3
+0.3 0.00014806 6.0032e-06 8.31416e-05
+0.6 8.72099e-05 8.08133e-06 8.31416e-05
+0.75 7.37384e-05 9.38141e-06 8.31416e-05
+0.9 6.50704e-05 1.01538e-05 8.31416e-05
+1.05 5.63535e-05 1.19692e-05 8.31416e-05
+1.2 4.99443e-05 1.33485e-05 8.31416e-05
+1.35 4.57542e-05 1.41569e-05 8.31416e-05
+1.5 3.71365e-05 2.01545e-05 8.31416e-05
+1.8 3.21699e-05 2.13106e-05 8.31416e-05
+2.4 2.52504e-05 2.33487e-05 8.31416e-05
+3 1.95583e-05 2.58394e-05 8.31416e-05
+3.6 0 4.34504e-05 8.31416e-05
+END DIST
+
+Metal 4 OVER 1
+DIST count 12 width 0.3
+0.3 0.00014111 1.38723e-05 8.31416e-05
+0.6 8.1707e-05 1.43318e-05 8.31416e-05
+0.75 6.84653e-05 1.55604e-05 8.31416e-05
+0.9 6.02964e-05 1.57031e-05 8.31416e-05
+1.05 5.3374e-05 1.62431e-05 8.31416e-05
+1.2 4.68504e-05 1.79145e-05 8.31416e-05
+1.35 4.25354e-05 1.89832e-05 8.31416e-05
+1.5 3.61744e-05 2.28577e-05 8.31416e-05
+1.8 3.09981e-05 2.44961e-05 8.31416e-05
+2.4 2.4072e-05 2.71202e-05 8.31416e-05
+3 1.86949e-05 2.98237e-05 8.31416e-05
+3.6 0 4.6886e-05 8.31416e-05
+END DIST
+
+Metal 4 OVER 2
+DIST count 12 width 0.3
+0.3 0.000140269 1.5854e-05 8.31416e-05
+0.6 7.95803e-05 1.82502e-05 8.31416e-05
+0.75 6.60577e-05 2.0166e-05 8.31416e-05
+0.9 5.75943e-05 2.08744e-05 8.31416e-05
+1.05 5.04916e-05 2.17562e-05 8.31416e-05
+1.2 4.40156e-05 2.35596e-05 8.31416e-05
+1.35 3.97364e-05 2.47136e-05 8.31416e-05
+1.5 3.3428e-05 2.86658e-05 8.31416e-05
+1.8 2.82882e-05 3.05424e-05 8.31416e-05
+2.4 2.12886e-05 3.37244e-05 8.31416e-05
+3 1.59798e-05 3.67443e-05 8.31416e-05
+3.6 0 5.13943e-05 8.31416e-05
+END DIST
+
+Metal 4 OVER 3
+DIST count 12 width 0.3
+0.3 0.000134986 2.28838e-05 8.31416e-05
+0.6 6.96619e-05 3.38989e-05 8.31416e-05
+0.75 5.72228e-05 3.7435e-05 8.31416e-05
+0.9 4.84984e-05 4.06081e-05 8.31416e-05
+1.05 4.09459e-05 4.37168e-05 8.31416e-05
+1.2 3.44564e-05 4.69265e-05 8.31416e-05
+1.35 3.02674e-05 4.88497e-05 8.31416e-05
+1.5 2.42707e-05 5.31037e-05 8.31416e-05
+1.8 2.00089e-05 5.48545e-05 8.31416e-05
+2.4 1.37294e-05 5.79563e-05 8.31416e-05
+3 9.76125e-06 6.05071e-05 8.31416e-05
+3.6 0 7.07783e-05 8.31416e-05
+END DIST
+
+Metal 4 UNDER
+WIDTH Table 1 entries:  0.3
+
+Metal 4 UNDER 5
+DIST count 12 width 0.3
+0.3 0.000135316 2.36301e-05 8.31416e-05
+0.6 6.7595e-05 3.84026e-05 8.31416e-05
+0.75 5.37506e-05 4.24264e-05 8.31416e-05
+0.9 4.48935e-05 4.50291e-05 8.31416e-05
+1.05 3.71003e-05 4.78004e-05 8.31416e-05
+1.2 3.05564e-05 5.08231e-05 8.31416e-05
+1.35 2.61856e-05 5.28313e-05 8.31416e-05
+1.5 2.00076e-05 5.73068e-05 8.31416e-05
+1.8 1.53513e-05 5.98066e-05 8.31416e-05
+2.4 1.05063e-05 6.30361e-05 8.31416e-05
+3 7.41771e-06 6.59394e-05 8.31416e-05
+3.6 0 7.35747e-05 8.31416e-05
+END DIST
+
+Metal 4 UNDER 6
+DIST count 12 width 0.3
+0.3 0.000140175 1.46544e-05 8.31416e-05
+0.6 7.96936e-05 1.62943e-05 8.31416e-05
+0.75 6.66489e-05 1.76954e-05 8.31416e-05
+0.9 5.85025e-05 1.82527e-05 8.31416e-05
+1.05 5.14615e-05 1.91808e-05 8.31416e-05
+1.2 4.47278e-05 2.11824e-05 8.31416e-05
+1.35 4.00772e-05 2.25606e-05 8.31416e-05
+1.5 3.34277e-05 2.66501e-05 8.31416e-05
+1.8 2.76247e-05 2.87572e-05 8.31416e-05
+2.4 2.01469e-05 3.18759e-05 8.31416e-05
+3 1.50997e-05 3.45089e-05 8.31416e-05
+3.6 0 4.83455e-05 8.31416e-05
+END DIST
+
+Metal 4 DIAGUNDER
+WIDTH Table 1 entries:  0.3
+
+Metal 4 DIAGUNDER 5
+DIST count 13 width 0.3
+0 0 3.85072e-05 8.31416e-05
+0.3 0 3.01556e-05 8.31416e-05
+0.6 0 2.5149e-05 8.31416e-05
+0.75 0 2.30367e-05 8.31416e-05
+0.9 0 2.12534e-05 8.31416e-05
+1.05 0 1.9782e-05 8.31416e-05
+1.2 0 1.82214e-05 8.31416e-05
+1.35 0 1.72774e-05 8.31416e-05
+1.5 0 1.65106e-05 8.31416e-05
+1.8 0 1.53432e-05 8.31416e-05
+2.4 0 1.36758e-05 8.31416e-05
+3 0 1.22058e-05 8.31416e-05
+3.6 0 1.081e-05 8.31416e-05
+END DIST
+
+Metal 4 DIAGUNDER 6
+DIST count 13 width 0.3
+0 0 2.21675e-05 8.31416e-05
+0.3 0 2.00002e-05 8.31416e-05
+0.6 0 1.91215e-05 8.31416e-05
+0.75 0 1.86582e-05 8.31416e-05
+0.9 0 1.81798e-05 8.31416e-05
+1.05 0 1.76901e-05 8.31416e-05
+1.2 0 1.68822e-05 8.31416e-05
+1.35 0 1.63993e-05 8.31416e-05
+1.5 0 1.59259e-05 8.31416e-05
+1.8 0 1.50316e-05 8.31416e-05
+2.4 0 1.35353e-05 8.31416e-05
+3 0 1.24338e-05 8.31416e-05
+3.6 0 1.16745e-05 8.31416e-05
+END DIST
+
+Metal 4 OVERUNDER
+WIDTH Table 1 entries:  0.3
+
+Metal 4 OVER 1 UNDER 5
+DIST count 12 width 0.3
+0.3 0.00013111 2.80842e-05 8.31416e-05
+0.6 6.3731e-05 4.26296e-05 8.31416e-05
+0.75 4.98984e-05 4.67934e-05 8.31416e-05
+0.9 4.12459e-05 4.91916e-05 8.31416e-05
+1.05 3.44301e-05 5.13265e-05 8.31416e-05
+1.2 2.79386e-05 5.44243e-05 8.31416e-05
+1.35 2.36e-05 5.64935e-05 8.31416e-05
+1.5 1.87452e-05 5.97169e-05 8.31416e-05
+1.8 1.41858e-05 6.2241e-05 8.31416e-05
+2.4 9.61199e-06 6.5411e-05 8.31416e-05
+3 6.7497e-06 6.83087e-05 8.31416e-05
+3.6 0 7.54357e-05 8.31416e-05
+END DIST
+
+Metal 4 OVER 1 UNDER 6
+DIST count 12 width 0.3
+0.3 0.000136187 1.91389e-05 8.31416e-05
+0.6 7.62508e-05 2.01372e-05 8.31416e-05
+0.75 6.32123e-05 2.166e-05 8.31416e-05
+0.9 5.52146e-05 2.19936e-05 8.31416e-05
+1.05 4.90399e-05 2.23067e-05 8.31416e-05
+1.2 4.23351e-05 2.43965e-05 8.31416e-05
+1.35 3.77281e-05 2.58409e-05 8.31416e-05
+1.5 3.23347e-05 2.88066e-05 8.31416e-05
+1.8 2.65807e-05 3.11811e-05 8.31416e-05
+2.4 1.90483e-05 3.50262e-05 8.31416e-05
+3 1.38838e-05 3.82661e-05 8.31416e-05
+3.6 0 5.10799e-05 8.31416e-05
+END DIST
+
+Metal 4 OVER 2 UNDER 5
+DIST count 12 width 0.3
+0.3 0.000130463 3.46028e-05 8.31416e-05
+0.6 6.14767e-05 5.13019e-05 8.31416e-05
+0.75 4.76882e-05 5.56297e-05 8.31416e-05
+0.9 3.90712e-05 5.83052e-05 8.31416e-05
+1.05 3.22341e-05 6.07652e-05 8.31416e-05
+1.2 2.57497e-05 6.41631e-05 8.31416e-05
+1.35 2.13483e-05 6.64895e-05 8.31416e-05
+1.5 1.64351e-05 6.99231e-05 8.31416e-05
+1.8 1.16403e-05 7.28914e-05 8.31416e-05
+2.4 6.74808e-06 7.66804e-05 8.31416e-05
+3 4.16879e-06 7.95897e-05 8.31416e-05
+3.6 0 8.44473e-05 8.31416e-05
+END DIST
+
+Metal 4 OVER 2 UNDER 6
+DIST count 12 width 0.3
+0.3 0.000135514 2.16781e-05 8.31416e-05
+0.6 7.419e-05 2.46774e-05 8.31416e-05
+0.75 6.09601e-05 2.66896e-05 8.31416e-05
+0.9 5.27865e-05 2.74323e-05 8.31416e-05
+1.05 4.65146e-05 2.8029e-05 8.31416e-05
+1.2 3.99084e-05 3.02435e-05 8.31416e-05
+1.35 3.53859e-05 3.17739e-05 8.31416e-05
+1.5 3.00802e-05 3.48175e-05 8.31416e-05
+1.8 2.44429e-05 3.73692e-05 8.31416e-05
+2.4 1.69841e-05 4.15359e-05 8.31416e-05
+3 1.19713e-05 4.48686e-05 8.31416e-05
+3.6 0 5.59586e-05 8.31416e-05
+END DIST
+
+Metal 4 OVER 3 UNDER 5
+DIST count 12 width 0.3
+0.3 0.000124023 4.81241e-05 8.31416e-05
+0.6 5.5732e-05 6.81708e-05 8.31416e-05
+0.75 4.23479e-05 7.49865e-05 8.31416e-05
+0.9 3.3955e-05 7.98024e-05 8.31416e-05
+1.05 2.75282e-05 8.64411e-05 8.31416e-05
+1.2 2.18668e-05 9.0314e-05 8.31416e-05
+1.35 1.83112e-05 9.26748e-05 8.31416e-05
+1.5 1.43465e-05 9.57613e-05 8.31416e-05
+1.8 1.10711e-05 9.77788e-05 8.31416e-05
+2.4 7.06034e-06 0.000100394 8.31416e-05
+3 4.69938e-06 0.000103191 8.31416e-05
+3.6 0 0.000110319 8.31416e-05
+END DIST
+
+Metal 4 OVER 3 UNDER 6
+DIST count 12 width 0.3
+0.3 0.000130844 2.78049e-05 8.31416e-05
+0.6 6.47308e-05 4.00049e-05 8.31416e-05
+0.75 5.24353e-05 4.338e-05 8.31416e-05
+0.9 4.37548e-05 4.66835e-05 8.31416e-05
+1.05 3.70952e-05 4.97306e-05 8.31416e-05
+1.2 2.80431e-05 5.45739e-05 8.31416e-05
+1.35 2.3947e-05 5.67216e-05 8.31416e-05
+1.5 1.93211e-05 5.98575e-05 8.31416e-05
+1.8 1.49852e-05 6.20868e-05 8.31416e-05
+2.4 8.75022e-06 6.57655e-05 8.31416e-05
+3 5.19751e-06 6.83347e-05 8.31416e-05
+3.6 0 7.4217e-05 8.31416e-05
+END DIST
+
+Metal 5 RESOVER
+WIDTH Table 1 entries:  0.3
+
+Metal 5 RESOVER 0
+DIST count 55 width 0.3
+0 0 0 0.000168094
+0 0.66 0 0.000168094
+0 0.96 0 0.000168094
+0 1.47 0 0.000168094
+0 1.62 0 0.000168094
+0 1.92 0 0.000168094
+0 2.43 0 0.000168094
+0 2.58 0 0.000168094
+0 2.88 0 0.000168094
+0 3.84 0 0.000168094
+0.66 0.66 0 0.000168094
+0.66 0.96 0 0.000168094
+0.66 1.47 0 0.000168094
+0.66 1.62 0 0.000168094
+0.66 1.92 0 0.000168094
+0.66 2.43 0 0.000168094
+0.66 2.58 0 0.000168094
+0.66 2.88 0 0.000168094
+0.66 3.84 0 0.000168094
+0.96 0.96 0 0.000168094
+0.96 1.47 0 0.000168094
+0.96 1.62 0 0.000168094
+0.96 1.92 0 0.000168094
+0.96 2.43 0 0.000168094
+0.96 2.58 0 0.000168094
+0.96 2.88 0 0.000168094
+0.96 3.84 0 0.000168094
+1.47 1.47 0 0.000168094
+1.47 1.62 0 0.000168094
+1.47 1.92 0 0.000168094
+1.47 2.43 0 0.000168094
+1.47 2.58 0 0.000168094
+1.47 2.88 0 0.000168094
+1.47 3.84 0 0.000168094
+1.62 1.62 0 0.000168094
+1.62 1.92 0 0.000168094
+1.62 2.43 0 0.000168094
+1.62 2.58 0 0.000168094
+1.62 2.88 0 0.000168094
+1.62 3.84 0 0.000168094
+1.92 1.92 0 0.000168094
+1.92 2.43 0 0.000168094
+1.92 2.58 0 0.000168094
+1.92 2.88 0 0.000168094
+1.92 3.84 0 0.000168094
+2.43 2.43 0 0.000168094
+2.43 2.58 0 0.000168094
+2.43 2.88 0 0.000168094
+2.43 3.84 0 0.000168094
+2.58 2.58 0 0.000168094
+2.58 2.88 0 0.000168094
+2.58 3.84 0 0.000168094
+2.88 2.88 0 0.000168094
+2.88 3.84 0 0.000168094
+3.84 0 3.84 0.000168094
+END DIST
+
+Metal 5 RESOVER 1
+DIST count 0 width 0.3
+END DIST
+
+Metal 5 RESOVER 2
+DIST count 0 width 0.3
+END DIST
+
+Metal 5 RESOVER 3
+DIST count 0 width 0.3
+END DIST
+
+Metal 5 RESOVER 4
+DIST count 0 width 0.3
+END DIST
+
+Metal 5 OVER
+WIDTH Table 1 entries:  0.3
+
+Metal 5 OVER 0
+DIST count 12 width 0.3
+0.3 0.000146568 5.02495e-06 8.4047e-05
+0.6 8.65107e-05 7.72704e-06 8.4047e-05
+0.75 7.44525e-05 8.10027e-06 8.4047e-05
+0.9 6.91912e-05 6.92988e-06 8.4047e-05
+1.05 6.10527e-05 7.63648e-06 8.4047e-05
+1.2 4.90899e-05 1.52999e-05 8.4047e-05
+1.35 4.47325e-05 1.6183e-05 8.4047e-05
+1.5 4.10382e-05 1.70125e-05 8.4047e-05
+1.8 3.51456e-05 1.84732e-05 8.4047e-05
+2.4 2.72684e-05 2.05896e-05 8.4047e-05
+3 2.27245e-05 2.16227e-05 8.4047e-05
+3.6 0 4.21628e-05 8.4047e-05
+END DIST
+
+Metal 5 OVER 1
+DIST count 12 width 0.3
+0.3 0.000139132 1.26134e-05 8.4047e-05
+0.6 8.14751e-05 1.24631e-05 8.4047e-05
+0.75 6.96557e-05 1.26639e-05 8.4047e-05
+0.9 6.39559e-05 1.16561e-05 8.4047e-05
+1.05 5.80155e-05 1.14228e-05 8.4047e-05
+1.2 4.84838e-05 1.68488e-05 8.4047e-05
+1.35 4.4164e-05 1.77571e-05 8.4047e-05
+1.5 4.04466e-05 1.85645e-05 8.4047e-05
+1.8 3.44414e-05 1.98822e-05 8.4047e-05
+2.4 2.65257e-05 2.16293e-05 8.4047e-05
+3 2.22986e-05 2.24663e-05 8.4047e-05
+3.6 0 4.30286e-05 8.4047e-05
+END DIST
+
+Metal 5 OVER 2
+DIST count 12 width 0.3
+0.3 0.000138894 1.35323e-05 8.4047e-05
+0.6 8.12519e-05 1.35359e-05 8.4047e-05
+0.75 6.93507e-05 1.41118e-05 8.4047e-05
+0.9 6.32992e-05 1.31999e-05 8.4047e-05
+1.05 5.69962e-05 1.29368e-05 8.4047e-05
+1.2 4.73217e-05 1.82256e-05 8.4047e-05
+1.35 4.29978e-05 1.90478e-05 8.4047e-05
+1.5 3.93936e-05 1.98249e-05 8.4047e-05
+1.8 3.37407e-05 2.12537e-05 8.4047e-05
+2.4 2.61844e-05 2.36108e-05 8.4047e-05
+3 2.15726e-05 2.51067e-05 8.4047e-05
+3.6 0 4.46569e-05 8.4047e-05
+END DIST
+
+Metal 5 OVER 3
+DIST count 12 width 0.3
+0.3 0.000140746 1.25047e-05 8.4047e-05
+0.6 8.04344e-05 1.50092e-05 8.4047e-05
+0.75 6.85846e-05 1.55484e-05 8.4047e-05
+0.9 6.25594e-05 1.46735e-05 8.4047e-05
+1.05 5.62133e-05 1.45239e-05 8.4047e-05
+1.2 4.70636e-05 1.9347e-05 8.4047e-05
+1.35 4.25966e-05 2.03596e-05 8.4047e-05
+1.5 3.88407e-05 2.13303e-05 8.4047e-05
+1.8 3.29027e-05 2.31458e-05 8.4047e-05
+2.4 2.49448e-05 2.623e-05 8.4047e-05
+3 2.01555e-05 2.832e-05 8.4047e-05
+3.6 0 4.68405e-05 8.4047e-05
+END DIST
+
+Metal 5 OVER 4
+DIST count 12 width 0.3
+0.3 0.000130323 2.7435e-05 8.4047e-05
+0.6 7.04223e-05 3.27562e-05 8.4047e-05
+0.75 5.49589e-05 3.93818e-05 8.4047e-05
+0.9 4.83178e-05 4.11065e-05 8.4047e-05
+1.05 4.12148e-05 4.33268e-05 8.4047e-05
+1.2 3.03307e-05 5.11997e-05 8.4047e-05
+1.35 2.59923e-05 5.32006e-05 8.4047e-05
+1.5 2.2525e-05 5.4826e-05 8.4047e-05
+1.8 1.75039e-05 5.72524e-05 8.4047e-05
+2.4 1.2014e-05 6.00704e-05 8.4047e-05
+3 9.63567e-06 6.14787e-05 8.4047e-05
+3.6 0 7.15441e-05 8.4047e-05
+END DIST
+
+Metal 5 UNDER
+WIDTH Table 1 entries:  0.3
+
+Metal 5 UNDER 6
+DIST count 12 width 0.3
+0.3 0.000135394 1.99172e-05 8.4047e-05
+0.6 7.39111e-05 2.64392e-05 8.4047e-05
+0.75 6.12317e-05 2.88519e-05 8.4047e-05
+0.9 5.4377e-05 2.97303e-05 8.4047e-05
+1.05 4.71828e-05 3.13796e-05 8.4047e-05
+1.2 3.38324e-05 4.13796e-05 8.4047e-05
+1.35 2.91927e-05 4.35272e-05 8.4047e-05
+1.5 2.41842e-05 4.66622e-05 8.4047e-05
+1.8 1.84051e-05 4.9899e-05 8.4047e-05
+2.4 1.12487e-05 5.44266e-05 8.4047e-05
+3 7.63229e-06 5.67835e-05 8.4047e-05
+3.6 0 6.38373e-05 8.4047e-05
+END DIST
+
+Metal 5 DIAGUNDER
+WIDTH Table 1 entries:  0.3
+
+Metal 5 DIAGUNDER 6
+DIST count 13 width 0.3
+0 0 4.72749e-05 8.4047e-05
+0.3 0 3.51949e-05 8.4047e-05
+0.6 0 3.04805e-05 8.4047e-05
+0.75 0 2.825e-05 8.4047e-05
+0.9 0 2.61827e-05 8.4047e-05
+1.05 0 2.43165e-05 8.4047e-05
+1.2 0 2.26714e-05 8.4047e-05
+1.35 0 2.12505e-05 8.4047e-05
+1.5 0 2.00436e-05 8.4047e-05
+1.8 0 1.81939e-05 8.4047e-05
+2.4 0 1.60884e-05 8.4047e-05
+3 0 1.50833e-05 8.4047e-05
+3.6 0 1.44881e-05 8.4047e-05
+END DIST
+
+Metal 5 OVERUNDER
+WIDTH Table 1 entries:  0.3
+
+Metal 5 OVER 1 UNDER 6
+DIST count 12 width 0.3
+0.3 0.000130415 2.48133e-05 8.4047e-05
+0.6 7.05944e-05 2.94872e-05 8.4047e-05
+0.75 5.80652e-05 3.17832e-05 8.4047e-05
+0.9 5.10577e-05 3.27111e-05 8.4047e-05
+1.05 4.50728e-05 3.3816e-05 8.4047e-05
+1.2 3.3103e-05 4.25359e-05 8.4047e-05
+1.35 2.86203e-05 4.45457e-05 8.4047e-05
+1.5 2.37263e-05 4.75243e-05 8.4047e-05
+1.8 1.80881e-05 5.04655e-05 8.4047e-05
+2.4 1.1084e-05 5.46675e-05 8.4047e-05
+3 7.59864e-06 5.70364e-05 8.4047e-05
+3.6 0 6.4353e-05 8.4047e-05
+END DIST
+
+Metal 5 OVER 2 UNDER 6
+DIST count 12 width 0.3
+0.3 0.000130619 2.48189e-05 8.4047e-05
+0.6 6.96794e-05 3.06705e-05 8.4047e-05
+0.75 5.74717e-05 3.28595e-05 8.4047e-05
+0.9 5.05803e-05 3.36095e-05 8.4047e-05
+1.05 4.45757e-05 3.45797e-05 8.4047e-05
+1.2 3.2531e-05 4.32459e-05 8.4047e-05
+1.35 2.79494e-05 4.5307e-05 8.4047e-05
+1.5 2.29816e-05 4.83948e-05 8.4047e-05
+1.8 1.72821e-05 5.16195e-05 8.4047e-05
+2.4 1.02488e-05 5.63096e-05 8.4047e-05
+3 6.59638e-06 5.89457e-05 8.4047e-05
+3.6 0 6.50934e-05 8.4047e-05
+END DIST
+
+Metal 5 OVER 3 UNDER 6
+DIST count 12 width 0.3
+0.3 0.000131118 2.47014e-05 8.4047e-05
+0.6 6.94604e-05 3.13388e-05 8.4047e-05
+0.75 5.69323e-05 3.38298e-05 8.4047e-05
+0.9 4.9774e-05 3.48387e-05 8.4047e-05
+1.05 4.36041e-05 3.60161e-05 8.4047e-05
+1.2 3.18776e-05 4.44441e-05 8.4047e-05
+1.35 2.73375e-05 4.65611e-05 8.4047e-05
+1.5 2.24295e-05 4.96856e-05 8.4047e-05
+1.8 1.68119e-05 5.30052e-05 8.4047e-05
+2.4 9.73429e-06 5.8046e-05 8.4047e-05
+3 5.9483e-06 6.1081e-05 8.4047e-05
+3.6 0 6.67856e-05 8.4047e-05
+END DIST
+
+Metal 5 OVER 4 UNDER 6
+DIST count 12 width 0.3
+0.3 0.000121594 3.98517e-05 8.4047e-05
+0.6 5.85944e-05 5.19191e-05 8.4047e-05
+0.75 4.42835e-05 5.86967e-05 8.4047e-05
+0.9 3.6782e-05 6.21109e-05 8.4047e-05
+1.05 3.02917e-05 6.69761e-05 8.4047e-05
+1.2 2.21398e-05 7.30627e-05 8.4047e-05
+1.35 1.81325e-05 7.5572e-05 8.4047e-05
+1.5 1.50838e-05 7.75133e-05 8.4047e-05
+1.8 1.08526e-05 8.04382e-05 8.4047e-05
+2.4 6.6501e-06 8.36963e-05 8.4047e-05
+3 4.87423e-06 8.54392e-05 8.4047e-05
+3.6 0 9.10599e-05 8.4047e-05
+END DIST
+
+Metal 6 RESOVER
+WIDTH Table 1 entries:  1.6
+
+Metal 6 RESOVER 0
+DIST count 55 width 1.6
+0 0 0 1.83563e-05
+0 1.73 0 1.83563e-05
+0 3.33 0 1.83563e-05
+0 4.26 0 1.83563e-05
+0 5.06 0 1.83563e-05
+0 6.66 0 1.83563e-05
+0 7.59 0 1.83563e-05
+0 8.39 0 1.83563e-05
+0 9.99 0 1.83563e-05
+0 13.32 0 1.83563e-05
+1.73 1.73 0 1.83563e-05
+1.73 3.33 0 1.83563e-05
+1.73 4.26 0 1.83563e-05
+1.73 5.06 0 1.83563e-05
+1.73 6.66 0 1.83563e-05
+1.73 7.59 0 1.83563e-05
+1.73 8.39 0 1.83563e-05
+1.73 9.99 0 1.83563e-05
+1.73 13.32 0 1.83563e-05
+3.33 3.33 0 1.83563e-05
+3.33 4.26 0 1.83563e-05
+3.33 5.06 0 1.83563e-05
+3.33 6.66 0 1.83563e-05
+3.33 7.59 0 1.83563e-05
+3.33 8.39 0 1.83563e-05
+3.33 9.99 0 1.83563e-05
+3.33 13.32 0 1.83563e-05
+4.26 4.26 0 1.83563e-05
+4.26 5.06 0 1.83563e-05
+4.26 6.66 0 1.83563e-05
+4.26 7.59 0 1.83563e-05
+4.26 8.39 0 1.83563e-05
+4.26 9.99 0 1.83563e-05
+4.26 13.32 0 1.83563e-05
+5.06 5.06 0 1.83563e-05
+5.06 6.66 0 1.83563e-05
+5.06 7.59 0 1.83563e-05
+5.06 8.39 0 1.83563e-05
+5.06 9.99 0 1.83563e-05
+5.06 13.32 0 1.83563e-05
+6.66 6.66 0 1.83563e-05
+6.66 7.59 0 1.83563e-05
+6.66 8.39 0 1.83563e-05
+6.66 9.99 0 1.83563e-05
+6.66 13.32 0 1.83563e-05
+7.59 7.59 0 1.83563e-05
+7.59 8.39 0 1.83563e-05
+7.59 9.99 0 1.83563e-05
+7.59 13.32 0 1.83563e-05
+8.39 8.39 0 1.83563e-05
+8.39 9.99 0 1.83563e-05
+8.39 13.32 0 1.83563e-05
+9.99 9.99 0 1.83563e-05
+9.99 13.32 0 1.83563e-05
+13.32 0 13.32 1.83563e-05
+END DIST
+
+Metal 6 RESOVER 1
+DIST count 0 width 1.6
+END DIST
+
+Metal 6 RESOVER 2
+DIST count 0 width 1.6
+END DIST
+
+Metal 6 RESOVER 3
+DIST count 0 width 1.6
+END DIST
+
+Metal 6 RESOVER 4
+DIST count 0 width 1.6
+END DIST
+
+Metal 6 RESOVER 5
+DIST count 0 width 1.6
+END DIST
+
+Metal 6 OVER
+WIDTH Table 1 entries:  1.6
+
+Metal 6 OVER 0
+DIST count 12 width 1.6
+1.6 7.70727e-05 1.28051e-05 9.17806e-06
+3.2 4.16244e-05 1.8829e-05 9.17806e-06
+4 3.48304e-05 2.0061e-05 9.17806e-06
+4.8 2.63231e-05 2.44747e-05 9.17806e-06
+5.6 2.11476e-05 2.79939e-05 9.17806e-06
+6.4 1.8024e-05 2.94438e-05 9.17806e-06
+7.2 1.52959e-05 3.08495e-05 9.17806e-06
+8 1.31579e-05 3.18988e-05 9.17806e-06
+9.6 0 4.2381e-05 9.17806e-06
+12.8 0 4.2381e-05 9.17806e-06
+16 0 4.2381e-05 9.17806e-06
+19.2 0 4.2381e-05 9.17806e-06
+END DIST
+
+Metal 6 OVER 1
+DIST count 12 width 1.6
+1.6 7.19495e-05 1.88937e-05 9.17806e-06
+3.2 3.77517e-05 2.41014e-05 9.17806e-06
+4 3.12477e-05 2.54782e-05 9.17806e-06
+4.8 2.41341e-05 2.88483e-05 9.17806e-06
+5.6 2.0153e-05 3.08419e-05 9.17806e-06
+6.4 1.72664e-05 3.20493e-05 9.17806e-06
+7.2 1.49348e-05 3.3145e-05 9.17806e-06
+8 1.29865e-05 3.41298e-05 9.17806e-06
+9.6 0 4.51026e-05 9.17806e-06
+12.8 0 4.51026e-05 9.17806e-06
+16 0 4.51026e-05 9.17806e-06
+19.2 0 4.51026e-05 9.17806e-06
+END DIST
+
+Metal 6 OVER 2
+DIST count 12 width 1.6
+1.6 7.10698e-05 2.03921e-05 9.17806e-06
+3.2 3.71086e-05 2.56048e-05 9.17806e-06
+4 3.09144e-05 2.68625e-05 9.17806e-06
+4.8 2.38044e-05 3.0346e-05 9.17806e-06
+5.6 1.95741e-05 3.26598e-05 9.17806e-06
+6.4 1.63623e-05 3.43051e-05 9.17806e-06
+7.2 1.36436e-05 3.6008e-05 9.17806e-06
+8 1.15478e-05 3.73638e-05 9.17806e-06
+9.6 0 4.69941e-05 9.17806e-06
+12.8 0 4.69941e-05 9.17806e-06
+16 0 4.69941e-05 9.17806e-06
+19.2 0 4.69943e-05 9.17806e-06
+END DIST
+
+Metal 6 OVER 3
+DIST count 12 width 1.6
+1.6 7.02531e-05 2.24514e-05 9.17806e-06
+3.2 3.64029e-05 2.78811e-05 9.17806e-06
+4 3.02924e-05 2.92348e-05 9.17806e-06
+4.8 2.33056e-05 3.29001e-05 9.17806e-06
+5.6 1.89839e-05 3.55508e-05 9.17806e-06
+6.4 1.56356e-05 3.74956e-05 9.17806e-06
+7.2 1.27249e-05 3.94579e-05 9.17806e-06
+8 1.04566e-05 4.11284e-05 9.17806e-06
+9.6 0 4.95055e-05 9.17806e-06
+12.8 0 4.95055e-05 9.17806e-06
+16 0 4.95055e-05 9.17806e-06
+19.2 0 4.95055e-05 9.17806e-06
+END DIST
+
+Metal 6 OVER 4
+DIST count 12 width 1.6
+1.6 6.80015e-05 3.08859e-05 9.17806e-06
+3.2 3.36584e-05 3.85538e-05 9.17806e-06
+4 2.75006e-05 4.04561e-05 9.17806e-06
+4.8 2.0938e-05 4.40865e-05 9.17806e-06
+5.6 1.71447e-05 4.65752e-05 9.17806e-06
+6.4 1.43547e-05 4.82697e-05 9.17806e-06
+7.2 1.19269e-05 4.99141e-05 9.17806e-06
+8 1.01083e-05 5.09463e-05 9.17806e-06
+9.6 0 5.93995e-05 9.17806e-06
+12.8 0 5.93995e-05 9.17806e-06
+16 0 5.93995e-05 9.17806e-06
+19.2 0 5.93995e-05 9.17806e-06
+END DIST
+
+Metal 6 OVER 5
+DIST count 12 width 1.6
+1.6 4.59423e-05 8.66424e-05 9.17806e-06
+3.2 1.95217e-05 9.2759e-05 9.17806e-06
+4 1.68641e-05 9.08466e-05 9.17806e-06
+4.8 1.25324e-05 9.34708e-05 9.17806e-06
+5.6 1.03412e-05 9.52085e-05 9.17806e-06
+6.4 8.94776e-06 9.61762e-05 9.17806e-06
+7.2 7.93763e-06 9.69672e-05 9.17806e-06
+8 8.77156e-06 9.43731e-05 9.17806e-06
+9.6 0 0.000104426 9.17806e-06
+12.8 0 0.000104418 9.17806e-06
+16 0 0.000104412 9.17806e-06
+19.2 0 0.000104445 9.17806e-06
+END DIST
+
+Metal 6 UNDER
+WIDTH Table 0 entries: 
+
+Metal 6 DIAGUNDER
+WIDTH Table 0 entries: 
+END DensityModel 0
diff --git a/src/hammer-vlsi/technology/sky130/extra/sky130hd.tracks b/src/hammer-vlsi/technology/sky130/extra/sky130hd.tracks
new file mode 100644
index 000000000..39a0ffbab
--- /dev/null
+++ b/src/hammer-vlsi/technology/sky130/extra/sky130hd.tracks
@@ -0,0 +1,6 @@
+make_tracks li1 -x_offset 0.23 -x_pitch 0.46 -y_offset 0.17 -y_pitch 0.34
+make_tracks met1 -x_offset 0.17 -x_pitch 0.34 -y_offset 0.17 -y_pitch 0.34
+make_tracks met2 -x_offset 0.23 -x_pitch 0.46 -y_offset 0.23 -y_pitch 0.46
+make_tracks met3 -x_offset 0.34 -x_pitch 0.68 -y_offset 0.34 -y_pitch 0.68
+make_tracks met4 -x_offset 0.46 -x_pitch 0.92 -y_offset 0.46 -y_pitch 0.92
+make_tracks met5 -x_offset 1.70 -x_pitch 3.40 -y_offset 1.70 -y_pitch 3.40
diff --git a/src/hammer-vlsi/technology/sky130/sky130.tech.json b/src/hammer-vlsi/technology/sky130/sky130.tech.json
index 3365668bd..8f40d1b81 100644
--- a/src/hammer-vlsi/technology/sky130/sky130.tech.json
+++ b/src/hammer-vlsi/technology/sky130/sky130.tech.json
@@ -36,7 +36,8 @@
   "additional_lvs_text": "",
   "libraries": [
     {
-      "lef file": "tech-sky130-cache/sky130_fd_sc_hd.tlef",
+      "lef file": "tech-sky130-cache/sky130_fd_sc_hd__nom.tlef",
+      "klayout techfile": "tech-sky130-cache/sky130A.lyt",
       "spice file": "$SKY130_NDA/s8/V2.0.1/LVS/Calibre/source.cdl",
       "verilog sim": "tech-sky130-cache/primitives.v",
       "provides": [
@@ -411,45 +412,48 @@
   ],
   "dont use list": [
     "*sdf*",
-    "sky130_fd_sc_hd__probe_p_8"
+    "sky130_fd_sc_hd__probe_p_*",
+    "sky130_fd_sc_hd__probec_p_*"
   ],
   "special cells": [
     {
-      "cell_type": "tiehilocell",
-      "name": [
-        "sky130_fd_sc_hd__conb_1"
-      ]
+      "cell_type": "tiehilocell",    
+      "name": ["sky130_fd_sc_hd__conb_1"]
     },
     {
-      "cell_type": "endcap",
-      "name": [
-        "sky130_fd_sc_hd__tap_1"
-      ]
+      "cell_type": "tiehicell",      
+      "name": ["sky130_fd_sc_hd__conb_1"],
+      "input ports":  ["HI"]
     },
     {
-      "cell_type": "tapcell",
-      "name": [
-        "sky130_fd_sc_hd__tapvpwrvgnd_1"
-      ]
+      "cell_type": "tielocell",      
+      "name": ["sky130_fd_sc_hd__conb_1"],
+      "input ports":  ["LO"]
     },
     {
-      "cell_type": "stdfiller",
-      "name": [
-        "sky130_fd_sc_hd__fill_1",
-        "sky130_fd_sc_hd__fill_2",
-        "sky130_fd_sc_hd__fill_4",
-        "sky130_fd_sc_hd__fill_8"
-      ]
+      "cell_type": "endcap",         
+      "name": ["sky130_fd_sc_hd__tap_1"]
     },
     {
-      "cell_type": "decap",
-      "name": [
-        "sky130_fd_sc_hd__decap_3",
-        "sky130_fd_sc_hd__decap_4",
-        "sky130_fd_sc_hd__decap_6",
-        "sky130_fd_sc_hd__decap_8",
-        "sky130_fd_sc_hd__decap_12"
-      ]
+      "cell_type": "tapcell",        
+      "name": ["sky130_fd_sc_hd__tapvpwrvgnd_1"]
+    },
+    {
+      "cell_type": "stdfiller",      
+      "name": ["sky130_fd_sc_hd__fill_1", "sky130_fd_sc_hd__fill_2", "sky130_fd_sc_hd__fill_4", "sky130_fd_sc_hd__fill_8"]
+    },
+    {
+      "cell_type": "decap",          
+      "name": ["sky130_fd_sc_hd__decap_3", "sky130_fd_sc_hd__decap_4", "sky130_fd_sc_hd__decap_6", "sky130_fd_sc_hd__decap_8", "sky130_fd_sc_hd__decap_12"]
+    },
+    { 
+      "cell_type": "driver",         
+      "name": ["sky130_fd_sc_hd__buf_4"],
+      "input ports":  ["A"],
+      "output ports":  ["X"]
+    },
+    {"cell_type": "ctsbuffer",        
+      "name": ["sky130_fd_sc_hd__clkbuf_1"]
     }
   ],
   "stackups": [
@@ -577,4 +581,4 @@
       "y": 5.44
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/src/hammer-vlsi/technology/sky130/sram_compiler/__init__.py b/src/hammer-vlsi/technology/sky130/sram_compiler/__init__.py
index 2283023fb..e39b0d865 100644
--- a/src/hammer-vlsi/technology/sky130/sram_compiler/__init__.py
+++ b/src/hammer-vlsi/technology/sky130/sram_compiler/__init__.py
@@ -1,5 +1,6 @@
 
 import os, tempfile, subprocess
+from pathlib import Path
 
 from hammer_vlsi import MMMCCorner, MMMCCornerType, HammerTool, HammerToolStep, HammerSRAMGeneratorTool, SRAMParameters
 from hammer_vlsi.units import VoltageValue, TemperatureValue
@@ -16,7 +17,7 @@ def version_number(self, version: str) -> int:
 
     # Run generator for a single sram and corner
     def generate_sram(self, params: SRAMParameters, corner: MMMCCorner) -> ExtraLibrary:
-        tech_cache_dir = os.path.abspath(self.technology.cache_dir)
+        cache_dir = os.path.abspath(self.technology.cache_dir)
 
         #TODO: this is really an abuse of the corner stuff
         if corner.type == MMMCCornerType.Setup:
@@ -83,12 +84,15 @@ def generate_sram(self, params: SRAMParameters, corner: MMMCCorner) -> ExtraLibr
                 c=corner_str)
             if not os.path.exists(lib_path):
                 self.logger.error("SKY130 1rw1r SRAM cache does not support corner: {c}".format(c=corner_str))
+
+            self.setup_sram_spice(sram_name)
+            self.setup_sram_lef(sram_name)
             return ExtraLibrary(prefix=None, library=Library(
                 name=sram_name,
                 nldm_liberty_file=lib_path,
-                lef_file="{b}/{n}/{n}.lef".format(b=base_dir,n=sram_name),
+                lef_file="{b}/{n}/{n}.lef".format(b=cache_dir,n=sram_name),
                 gds_file="{b}/{n}/{n}.gds".format(b=base_dir,n=sram_name),
-                spice_file="{b}/{n}/{n}.lvs.sp".format(b=base_dir,n=sram_name),
+                spice_file="{b}/{n}/{n}.lvs.sp".format(b=cache_dir,n=sram_name),
                 verilog_sim="{b}/{n}/{n}.v".format(b=base_dir,n=sram_name),
                 corner={'nmos': speed_name, 'pmos': speed_name, 'temperature': str(corner.temp.value_in_units("C")) + " C"},
                 supplies={'VDD': str(corner.voltage.value_in_units("V")) + " V", 'GND': "0 V"},
@@ -97,4 +101,40 @@ def generate_sram(self, params: SRAMParameters, corner: MMMCCorner) -> ExtraLibr
             self.logger.error("SKY130 SRAM cache does not support family:{f}".format(f=params.family))
             return ExtraLibrary(prefix=None, library=None)
 
+    def setup_sram_spice(self,sram_name) -> None:
+        cache_dir = os.path.abspath(self.technology.cache_dir)
+        source_path = Path(self.get_setting("technology.sky130.openram_lib")) / sram_name / f"{sram_name}.lvs.sp"
+        dest_path = f"{os.path.abspath(self.technology.cache_dir)}/{sram_name}/{sram_name}.lvs.sp"
+        if not source_path.exists():
+            raise FileNotFoundError(f"SRAM Spice file not found: {source_path}")        
+        with open(source_path,'r') as sf:
+            with open(dest_path,'w') as df:
+                self.logger.info("Modifying SRAM SPICE deck: {} -> {}".format
+                    (source_path, dest_path))
+                for line in sf:
+                    line = line.replace('sky130_fd_pr__pfet_01v8','pshort')
+                    line = line.replace('sky130_fd_pr__nfet_01v8','nshort')
+                    if sram_name == "sky130_sram_1kbyte_1rw1r_8x1024_8":
+                        line = line.replace('wmask0[0]'    , 'wmask0')
+                    df.write(line)
+
+    def setup_sram_lef(self,sram_name) -> None:
+        source_path = Path(self.get_setting("technology.sky130.openram_lib")) / sram_name / f"{sram_name}.lef"
+        dest_path = f"{os.path.abspath(self.technology.cache_dir)}/{sram_name}/{sram_name}.lef"
+        if not source_path.exists():
+            raise FileNotFoundError(f"SRAM LEF file not found: {source_path}")
+        with open(source_path,'r') as sf:
+            with open(dest_path,'w') as df:
+                self.logger.info("Modifying SRAM LEF deck: {} -> {}".format
+                    (source_path, dest_path))
+                units=False
+                for line in sf:
+                    if line.strip().startswith("UNITS"):
+                        units=True
+                    if line.strip().startswith("END UNITS"):
+                        units=False
+                        continue
+                    if not units:
+                        df.write(line)
+
 tool=SKY130SRAMGenerator


Test patch:
diff --git a/src/hammer-vlsi/tech_test.py b/src/hammer-vlsi/tech_test.py
index 91b6072a4..9f7e6efa8 100644
--- a/src/hammer-vlsi/tech_test.py
+++ b/src/hammer-vlsi/tech_test.py
@@ -691,19 +691,29 @@ def add_special_cells(in_dict: Dict[str, Any]) -> Dict[str, Any]:
         tool.set_database(database)
 
         self.assertEqual(tool.technology.get_special_cell_by_type(CellType.TieHiCell),
-                [SpecialCell(name=list(["cell1"]), cell_type=CellType.TieHiCell, size=None),
-                 SpecialCell(name=list(["cell2"]), cell_type=CellType.TieHiCell, size=list(["1.5"]))
+                [SpecialCell(name=list(["cell1"]), cell_type=CellType.TieHiCell, size=None, input_ports=None, output_ports=None),
+                 SpecialCell(name=list(["cell2"]), cell_type=CellType.TieHiCell, size=list(["1.5"]), input_ports=None, output_ports=None)
                 ])
 
         self.assertEqual(tool.technology.get_special_cell_by_type(CellType.IOFiller),
-                [SpecialCell(name=list(["cell3"]), cell_type=CellType.IOFiller, size=list(["0.5"])),
+                [SpecialCell(name=list(["cell3"]), cell_type=CellType.IOFiller, size=list(["0.5"]), input_ports=list(["A","B"]), output_ports=list(["Y"]) )
                 ])
 
         self.assertEqual(tool.technology.get_special_cell_by_type(CellType.StdFiller),
-                [SpecialCell(name=list(["cell4"]), cell_type=CellType.StdFiller, size=None)])
+                [SpecialCell(name=list(["cell4"]), cell_type=CellType.StdFiller, size=None, input_ports=list(["A"]), output_ports=list(["Y","Z"]) )
+                ])
 
         self.assertEqual(tool.technology.get_special_cell_by_type(CellType.EndCap),
-                [SpecialCell(name=list(["cell5"]), cell_type=CellType.EndCap, size=None)])
+                [SpecialCell(name=list(["cell5"]), cell_type=CellType.EndCap, size=None, input_ports=None, output_ports=None )
+                ])
+
+        self.assertEqual(tool.technology.get_special_cell_by_type(CellType.TieLoCell),
+                [SpecialCell(name=list(["cell6"]), cell_type=CellType.TieLoCell, size=None, input_ports=None, output_ports=None )
+                ])
+
+        self.assertEqual(tool.technology.get_special_cell_by_type(CellType.TieHiCell),
+                [SpecialCell(name=list(["cell7"]), cell_type=CellType.TieHiCell, size=None, input_ports=None, output_ports=None )
+                ])
 
     def test_drc_lvs_decks(self) -> None:
         """
diff --git a/src/test/mypy.sh b/src/test/mypy.sh
index 873788a09..311e48692 100755
--- a/src/test/mypy.sh
+++ b/src/test/mypy.sh
@@ -17,8 +17,11 @@ call_mypy () {
 }
 
 # Core
-call_mypy --package hammer_vlsi
 call_mypy --package hammer_utils
+# call_mypy --package hammer_vlsi
+call_mypy ../hammer-vlsi/hammer_vlsi/*.py
+call_mypy ../hammer-vlsi/hammer_vlsi/vendor/__init__.py
+call_mypy ../hammer-vlsi/hammer_vlsi/vendor/openroad.py
 
 # Shell
 call_mypy ../hammer-shell/get-config
@@ -30,10 +33,12 @@ call_mypy ../hammer_config_test/test.py
 # Plugins
 call_mypy ../hammer-vlsi/synthesis/mocksynth/__init__.py
 call_mypy ../hammer-vlsi/synthesis/vivado/__init__.py
+call_mypy ../hammer-vlsi/synthesis/yosys/__init__.py
 call_mypy ../hammer-vlsi/synthesis/nop.py
 call_mypy ../hammer-vlsi/par/nop.py
 call_mypy ../hammer-vlsi/par/vivado/__init__.py
 call_mypy ../hammer-vlsi/par/mockpar/__init__.py
+call_mypy ../hammer-vlsi/par/openroad/__init__.py
 call_mypy ../hammer-vlsi/drc/*.py
 call_mypy ../hammer-vlsi/drc/openroad/__init__.py
 call_mypy ../hammer-vlsi/drc/magic/__init__.py