From 52ee3418e50d6797800c2542808eec419e032f16 Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Thu, 7 Mar 2024 19:08:05 -0800 Subject: [PATCH 01/68] implemented abstract method for tech plugins to generate their own library config --- hammer/par/openroad/__init__.py | 7 +- hammer/tech/__init__.py | 16 +- hammer/technology/sky130/__init__.py | 292 +++++++++++++++++- hammer/technology/sky130/defaults.yml | 6 +- hammer/technology/sky130/defaults_types.yml | 3 + .../sky130-tech-gen-files/beginning.json | 21 -- .../sky130-tech-gen-files/beginning_nda.json | 48 --- .../extra/sky130-tech-gen-files/cells.json | 54 ---- .../extra/sky130-tech-gen-files/sites.json | 6 - .../sky130-tech-gen-files/stackups-gen.py | 95 ------ .../extra/sky130-tech-gen-files/stackups.json | 112 ------- .../sky130/extra/sky130-tech-gen.py | 185 ----------- hammer/vlsi/driver.py | 14 +- 13 files changed, 315 insertions(+), 544 deletions(-) delete mode 100644 hammer/technology/sky130/extra/sky130-tech-gen-files/beginning.json delete mode 100644 hammer/technology/sky130/extra/sky130-tech-gen-files/beginning_nda.json delete mode 100644 hammer/technology/sky130/extra/sky130-tech-gen-files/cells.json delete mode 100644 hammer/technology/sky130/extra/sky130-tech-gen-files/sites.json delete mode 100755 hammer/technology/sky130/extra/sky130-tech-gen-files/stackups-gen.py delete mode 100644 hammer/technology/sky130/extra/sky130-tech-gen-files/stackups.json delete mode 100755 hammer/technology/sky130/extra/sky130-tech-gen.py diff --git a/hammer/par/openroad/__init__.py b/hammer/par/openroad/__init__.py index dfe3085c4..e19bad778 100644 --- a/hammer/par/openroad/__init__.py +++ b/hammer/par/openroad/__init__.py @@ -1448,7 +1448,12 @@ def write_gds(self) -> str: ilm_gds = list(map(lambda ilm: ilm.gds, self.get_input_ilms())) gds_files.extend(ilm_gds) - layer_map_file=self.get_setting('par.inputs.gds_map_file') + layer_map_file=self.get_gds_map_file() + if layer_map_file is None: + self.logger.error("Must have GDS layer map for macro PG library generation! Skipping.") + return True + else: + assert isinstance(layer_map_file, str) # the first entry of $KLAYOUT_PATH will be the one where the configuration is stored when KLayout exits # otherwise KLayout tries to write everything to the same directory as the klayout binary and throws an error if it is not writeable diff --git a/hammer/tech/__init__.py b/hammer/tech/__init__.py index 5044745de..f93d0a2b0 100644 --- a/hammer/tech/__init__.py +++ b/hammer/tech/__init__.py @@ -353,8 +353,14 @@ def __init__(self): self.time_unit: Optional[str] = None self.cap_unit: Optional[str] = None + @abstractmethod + def gen_config(self) -> None: + """For subclasses to set self.config directly, instead of from static JSON file""" + pass + @classmethod - def load_from_module(cls, tech_module: str) -> Optional["HammerTechnology"]: + #def load_from_module(cls, tech_module: str) -> Optional["HammerTechnology"]: + def load_from_module(cls, tech_module: str) -> "HammerTechnology": """Load a technology from a given module. :param tech_module: Technology module (e.g. "hammer.technology.asap7") @@ -366,17 +372,21 @@ def load_from_module(cls, tech_module: str) -> Optional["HammerTechnology"]: tech.name = technology_name tech.package = tech_module + #tech.config = tech.gen_config() tech_json = importlib.resources.files(tech_module) / f"{technology_name}.tech.json" tech_yaml = importlib.resources.files(tech_module) / f"{technology_name}.tech.yml" + #if tech.config is not None: # pydantic model already created + # print("Tech gen") + # return tech if tech_json.is_file(): tech.config = TechJSON.model_validate_json(tech_json.read_text()) return tech elif tech_yaml.is_file(): tech.config = TechJSON.model_validate_json(json.dumps(load_yaml(tech_yaml.read_text()))) return tech - else: #TODO - from Pydantic model instance - return None + else: # Assume tech implents gen_config() + return tech def get_lib_units(self) -> None: """ diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 35528bc7b..71a40c9e8 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -9,9 +9,13 @@ from typing import NamedTuple, List, Optional, Tuple, Dict, Set, Any import importlib import json +import functools -import hammer.tech -from hammer.tech import HammerTechnology +#import hammer.tech +from hammer.config import HammerDatabase +from hammer.tech import * +from hammer.tech.stackup import * +from hammer.tech.specialcells import * from hammer.vlsi import HammerTool, HammerPlaceAndRouteTool, TCLTool, HammerDRCTool, HammerLVSTool, \ HammerToolHookAction, HierarchicalMode @@ -23,6 +27,280 @@ class SKY130Tech(HammerTechnology): Override the HammerTechnology used in `hammer_tech.py` This class is loaded by function `load_from_json`, and will pass the `try` in `importlib`. """ + def gen_config(self) -> None: + """Generate the tech config, based on the library type selected""" + slib = self.get_setting("technology.sky130.stdcell_library") + SKY130A = self.get_setting("technology.sky130.sky130A") + + # Common tech LEF and IO cell spice netlists + libs = [ + Library(lef_file = "cache/sky130_fd_sc_hd__nom.tlef", verilog_sim = "cache/primitives.v", provides = [Provide(lib_type = "technology")]), + Library(spice_file = "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io__analog.spice", provides = [Provide(lib_type = "IO library")]) + ] + # Generate IO cells + library='sky130_fd_io' + SKYWATER_LIBS = os.path.join('$SKY130A', 'libs.ref', library) + LIBRARY_PATH = os.path.join( SKY130A, 'libs.ref', library, 'lib') + lib_corner_files=os.listdir(LIBRARY_PATH) + lib_corner_files.sort() + for cornerfilename in lib_corner_files: + # Skip versions with no internal power + if ('nointpwr' in cornerfilename) : continue + + tmp = cornerfilename.replace('.lib','') + # Split into cell, and corner strings + # Resulting list if only one ff/ss/tt in name: [, , , , ] + # Resulting list if ff_ff/ss_ss/tt_tt in name: [, , , , '', , , , ] + split_cell_corner = re.split('_(ff)|_(ss)|_(tt)', tmp) + cell_name = split_cell_corner[0] + process = split_cell_corner[1:-1] + temp_volt = split_cell_corner[-1].split('_')[1:] + + # Filter out cross corners (e.g ff_ss or ss_ff) + if len(process) > 3: + if not functools.reduce(lambda x,y: x and y, map(lambda p,q: p==q, process[0:3], process[4:]), True): + continue + # Determine actual corner + speed = next(c for c in process if c is not None).replace('_','') + if (speed == 'ff'): speed = 'fast' + if (speed == 'tt'): speed = 'typical' + if (speed == 'ss'): speed = 'slow' + + temp = temp_volt[0] + temp = temp.replace('n','-') + temp = temp.split('C')[0]+' C' + + vdd = ('.').join(temp_volt[1].split('v')) + ' V' + # Filter out IO/analog voltages that are not high voltage + if temp_volt[2].startswith('1'): continue + if len(temp_volt) == 4: + if temp_volt[3].startswith('1'): continue + + # gpiov2_pad_wrapped has separate GDS + if cell_name == 'sky130_ef_io__gpiov2_pad_wrapped': + file_lib = 'sky130_ef_io' + gds_file = cell_name + '.gds' + lef_file = 'cache/sky130_ef_io.lef' + spice_file = os.path.join(SKYWATER_LIBS,'cdl', file_lib + '.cdl') + elif 'sky130_ef_io' in cell_name: + file_lib = 'sky130_ef_io' + gds_file = file_lib + '.gds' + lef_file = 'cache/' + file_lib + '.lef' + spice_file = os.path.join(SKYWATER_LIBS,'cdl', file_lib + '.cdl') + else: + file_lib = library + gds_file = file_lib + '.gds' + lef_file = os.path.join(SKYWATER_LIBS,'lef', file_lib + '.lef') + spice_file = os.path.join(SKYWATER_LIBS,'spice', file_lib + '.spice') + + lib_entry = Library( + nldm_liberty_file = os.path.join(SKYWATER_LIBS,'lib', cornerfilename), + verilog_sim = os.path.join(SKYWATER_LIBS,'verilog', file_lib + '.v'), + lef_file = lef_file, + spice_file = spice_file, + gds_file = os.path.join(SKYWATER_LIBS,'gds', gds_file), + corner = Corner( + nmos = speed, + pmos = speed, + temperature = temp + ), + supplies = Supplies( + VDD = vdd, + GND ="0 V" + ), + provides = [Provide( + lib_type = cell_name, + vt = "RVT" + ) + ] + ) + libs.append(lib_entry) + + # Stdcell library-dependent lists + stackups = [] # type: List[Stackup] + phys_only = [] # type: List[Cell] + dont_use = [] # type: List[Cell] + spcl_cells = [] # type: List[SpecialCell] + + # Select standard cell libraries + if slib == "sky130_fd_sc_hd": + phys_only = [ + "sky130_fd_sc_hd__tap_1", "sky130_fd_sc_hd__tap_2", "sky130_fd_sc_hd__tapvgnd_1", "sky130_fd_sc_hd__tapvpwrvgnd_1", + "sky130_fd_sc_hd__fill_1", "sky130_fd_sc_hd__fill_2", "sky130_fd_sc_hd__fill_4", "sky130_fd_sc_hd__fill_8", + "sky130_fd_sc_hd__diode_2"] + dont_use = [ + "*sdf*", + "sky130_fd_sc_hd__probe_p_*", + "sky130_fd_sc_hd__probec_p_*" + ] + spcl_cells = [ + SpecialCell(cell_type = "tiehilocell", name = ["sky130_fd_sc_hd__conb_1"]), + SpecialCell(cell_type = "tiehicell", name = ["sky130_fd_sc_hd__conb_1"], output_ports = ["HI"]), + SpecialCell(cell_type = "tielocell", name = ["sky130_fd_sc_hd__conb_1"], output_ports = ["LO"]), + SpecialCell(cell_type = "endcap", name = ["sky130_fd_sc_hd__tap_1"]), + SpecialCell(cell_type = "tapcell", name = ["sky130_fd_sc_hd__tapvpwrvgnd_1"]), + SpecialCell(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"]), + SpecialCell(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"]), + SpecialCell(cell_type = "driver", name = ["sky130_fd_sc_hd__buf_4"], input_ports = ["A"], output_ports = ["X"]), + SpecialCell(cell_type = "ctsbuffer", name = ["sky130_fd_sc_hd__clkbuf_1"]) + ] + + # Generate standard cell library + library=slib + + SKYWATER_LIBS = os.path.join('$SKY130A', 'libs.ref', library) + LIBRARY_PATH = os.path.join( SKY130A, 'libs.ref', library, 'lib') + lib_corner_files=os.listdir(LIBRARY_PATH) + lib_corner_files.sort() + for cornerfilename in lib_corner_files: + if (not (library in cornerfilename) ) : continue + if ('ccsnoise' in cornerfilename): continue # ignore duplicate corner.lib/corner_ccsnoise.lib files + + tmp = cornerfilename.replace('.lib','') + if (tmp+'_ccsnoise.lib' in lib_corner_files): + cornerfilename=tmp+'_ccsnoise.lib' # use ccsnoise version of lib file + + cornername = tmp.split('__')[1] + cornerparts = cornername.split('_') + + speed = cornerparts[0] + if (speed == 'ff'): speed = 'fast' + if (speed == 'tt'): speed = 'typical' + if (speed == 'ss'): speed = 'slow' + + temp = cornerparts[1] + temp = temp.replace('n','-') + temp = temp.split('C')[0]+' C' + + vdd = cornerparts[2] + vdd = vdd.split('v')[0]+'.'+vdd.split('v')[1]+' V' + + lib_entry = Library( + nldm_liberty_file = os.path.join(SKYWATER_LIBS,'lib', cornerfilename), + verilog_sim = os.path.join('cache', library+'.v'), + lef_file = os.path.join(SKYWATER_LIBS,'lef', library+'.lef'), + spice_file = os.path.join('cache', library+'.cdl'), + gds_file = os.path.join(SKYWATER_LIBS,'gds', library+'.gds'), + corner = Corner( + nmos = speed, + pmos = speed, + temperature = temp + ), + supplies = Supplies( + VDD = vdd, + GND = "0 V" + ), + provides = [Provide( + lib_type = "stdcell", + vt = "RVT" + ) + ] + ) + + libs.append(lib_entry) + + # Generate stackup + metals = [] #type: List[Metal] + def is_float(string): + try: + float(string) + return True + except ValueError: + return False + + def get_min_from_line(line): + words = line.split() + nums = [float(w) for w in words if is_float(w)] + return min(nums) + + tlef_path = os.path.join(SKY130A, 'libs.ref', library, 'techlef', f"{library}__min.tlef") + with open(tlef_path, 'r') as f: + metal_name = None + metal_index = 0 + lines = f.readlines() + idx = -1 + while idx < len(lines): + idx += 1 + if idx == len(lines) - 1: break + line = lines[idx] + if '#' in line: line = line[:line.index('#')] + words = line.split() + if line.startswith('LAYER') and len(words) > 1: + if words[1].startswith('li') or words[1].startswith('met'): + metal_name = words[1] + metal_index += 1 + metal = {} + metal["name"] = metal_name + metal["index"] = metal_index + + if metal_name is not None: + line = line.strip() + if line.startswith("DIRECTION"): + metal["direction"] = words[1].lower() + if line.startswith("PITCH"): + metal["pitch"] = get_min_from_line(line) + if line.startswith("OFFSET"): + metal["offset"] = get_min_from_line(line) + if line.startswith("WIDTH"): + metal["min_width"] = get_min_from_line(line) + if line.startswith("SPACINGTABLE"): + metal["power_strap_widths_and_spacings"] = [] + while ';' not in line: + idx += 1 + if idx == len(lines) - 1: break + line = lines[idx].strip() + if '#' in line: line = line[:line.index('#')] + words = line.split() + d = {} + if line.startswith("WIDTH"): + d["width_at_least"] = float(words[1]) + d["min_spacing"] = float(words[2]) + metal["power_strap_widths_and_spacings"].append(d.copy()) + if line.startswith("END"): + metal["grid_unit"] = 0.001 + metals.append(Metal.model_validate(metal.copy())) + metal_name = None + stackups.append(Stackup(name = slib, grid_unit = Decimal("0.001"), metals = metals)) + + elif slib == "sky130_scl": + raise NotImplementedError(f"sky130_scl not yet supported") + else: + raise ValueError(f"Incorrect standard cell library selection: {slib}") + + self.config = TechJSON( + name = "Skywater 130nm Library", + grid_unit = "0.001", + shrink_factor = None, + installs = [ + PathPrefix(id = "$SKY130_NDA", path = "technology.sky130.sky130_nda"), + PathPrefix(id = "$SKY130A", path = "technology.sky130.sky130A"), + PathPrefix(id = "$SKY130_CDS", path = "technology.sky130.sky130_cds") + ], + libraries = libs, + gds_map_file = "extra/sky130_lefpin.map", + physical_only_cells_list = phys_only, + dont_use_list = dont_use, + drc_decks = [ + DRCDeck(tool_name = "calibre", deck_name = "calibre_drc", path = "$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules"), + DRCDeck(tool_name = "klayout", deck_name = "klayout_drc", path = "$SKY130A/libs.tech/klayout/drc/sky130A.lydrc"), + DRCDeck(tool_name = "pegasus", deck_name = "pegasus_drc", path = "$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl") + ], + additional_drc_text = "", + lvs_decks = [ + LVSDeck(tool_name = "calibre", deck_name = "calibre_lvs", path = "$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8"), + LVSDeck(tool_name = "pegasus", deck_name = "pegasus_lvs", path = "$SKY130_CDS/Sky130_LVS/Sky130_rev_0.0_0.1.lvs.pvl") + ], + additional_lvs_text = "", + tarballs = None, + sites = [ + Site(name = "unithd", x = Decimal("0.46"), y = Decimal("2.72")), + Site(name = "unithddbl", x = Decimal("0.46"), y = Decimal("5.44")) + ], + stackups = stackups, + special_cells = spcl_cells, + extra_prefixes = None + ) + def post_install_script(self) -> None: self.library_name = 'sky130_fd_sc_hd' # check whether variables were overriden to point to a valid path @@ -94,7 +372,7 @@ def setup_verilog(self) -> None: 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) - + # Additionally hack out the specifies sl = [] with open(dest_path, 'r') as sf: @@ -127,7 +405,7 @@ def setup_verilog(self) -> None: for list_idx, pattern_tuple in enumerate(pattern_idx): if list_idx != len(pattern_idx)-1: search_range = range(pattern_tuple[0]+1, pattern_idx[list_idx+1][0]) - else: + else: search_range = range(pattern_tuple[0]+1, len(sl)) for idx in search_range: list = re.findall(capture_pattern, sl[idx]) @@ -135,8 +413,8 @@ def setup_verilog(self) -> None: if elem != pattern_tuple[1]: sl[idx] = sl[idx].replace(elem, pattern_tuple[1]) self.logger.info(f"Incorrect reference `{elem}` to be replaced with: `{pattern_tuple[1]}` on raw line {idx}.") - - # Write back into destination + + # Write back into destination with open(dest_path, 'w') as df: df.writelines(sl) @@ -234,7 +512,7 @@ def setup_io_lefs(self) -> None: # Fix if idx_end_broken_macro < idx_start_next_macro: sl[idx_end_broken_macro] = end_fixed_macro - + df.writelines(sl) def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]: diff --git a/hammer/technology/sky130/defaults.yml b/hammer/technology/sky130/defaults.yml index 59bff2f12..5de945ec9 100644 --- a/hammer/technology/sky130/defaults.yml +++ b/hammer/technology/sky130/defaults.yml @@ -32,7 +32,8 @@ technology.sky130: io_file: "extra/efabless_template.io" # IO ring - take this template and modify for your own use io_file_meta: prependlocal - + + stdcell_library: "sky130_fd_sc_hd" # Choose between "sky130_fd_sc_hd" (open-source) or "sky130_scl" (Cadence) mentor.extra_env_vars_meta: lazydeepsubst # Mentor environment variables # Override this in project @@ -109,9 +110,6 @@ synthesis.yosys: par.inputs: gds_merge: true - gds_map_mode: manual - gds_map_file: "extra/sky130_lefpin.map" - gds_map_file_meta: prependlocal par.openroad: # openroad setup/files setrc_file: "extra/setRC.tcl" diff --git a/hammer/technology/sky130/defaults_types.yml b/hammer/technology/sky130/defaults_types.yml index f46c2e6f7..c3510631e 100644 --- a/hammer/technology/sky130/defaults_types.yml +++ b/hammer/technology/sky130/defaults_types.yml @@ -27,3 +27,6 @@ technology.sky130: # Path to IO file io_file: str + + # Choose between "sky130_fd_sc_hd" (open-source) or "sky130_scl" (Cadence) + stdcell_library: str diff --git a/hammer/technology/sky130/extra/sky130-tech-gen-files/beginning.json b/hammer/technology/sky130/extra/sky130-tech-gen-files/beginning.json deleted file mode 100644 index 7fbc7cd3c..000000000 --- a/hammer/technology/sky130/extra/sky130-tech-gen-files/beginning.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "Skywater 130nm Library", - "grid_unit": "0.001", - "installs": [ - { - "id": "$SKY130A", - "path": "technology.sky130.sky130A" - } - ], - "libraries": [ - { - "lef_file": "tech-sky130-cache/sky130_fd_sc_hd__nom.tlef", - "verilog_sim": "tech-sky130-cache/primitives.v", - "provides": [ - { - "lib_type": "technology" - } - ] - } - ] -} diff --git a/hammer/technology/sky130/extra/sky130-tech-gen-files/beginning_nda.json b/hammer/technology/sky130/extra/sky130-tech-gen-files/beginning_nda.json deleted file mode 100644 index 2d248220c..000000000 --- a/hammer/technology/sky130/extra/sky130-tech-gen-files/beginning_nda.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "Skywater 130nm Library", - "grid_unit": "0.001", - "installs": [ - { - "id": "$SKY130_NDA", - "path": "technology.sky130.sky130_nda" - }, - { - "id": "$SKY130A", - "path": "technology.sky130.sky130A" - } - ], - "layer_map_file": "$SKY130_NDA/s8/V2.0.1/VirtuosoOA/libs/technology_library/technology_library.layermap", - "drc_decks": [ - { - "tool_name": "calibre", - "deck_name": "calibre_drc", - "path": "$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules" - }, - { - "tool_name": "klayout", - "deck_name": "klayout_drc", - "path": "$SKY130A/libs.tech/klayout/drc/sky130A.lydrc" - } - ], - "additional_drc_text": "", - "lvs_decks": [ - { - "tool_name": "calibre", - "deck_name": "calibre_lvs", - "old_path": "$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8", - "path": "cache/lvsControlFile_s8" - } - ], - "additional_lvs_text": "", - "libraries": [ - { - "lef_file": "cache/sky130_fd_sc_hd__nom.tlef", - "verilog_sim": "cache/primitives.v", - "provides": [ - { - "lib_type": "technology" - } - ] - } - ] -} diff --git a/hammer/technology/sky130/extra/sky130-tech-gen-files/cells.json b/hammer/technology/sky130/extra/sky130-tech-gen-files/cells.json deleted file mode 100644 index 440b8afcf..000000000 --- a/hammer/technology/sky130/extra/sky130-tech-gen-files/cells.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "physical_only_cells_list": [ - "sky130_fd_sc_hd__tap_1", "sky130_fd_sc_hd__tap_2", "sky130_fd_sc_hd__tapvgnd_1", "sky130_fd_sc_hd__tapvpwrvgnd_1", - "sky130_fd_sc_hd__fill_1", "sky130_fd_sc_hd__fill_2", "sky130_fd_sc_hd__fill_4", "sky130_fd_sc_hd__fill_8", - "sky130_fd_sc_hd__diode_2" - ], - "dont_use_list": [ - "*sdf*", - "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": "tiehicell", - "name": ["sky130_fd_sc_hd__conb_1"], - "output_ports": ["HI"] - }, - { - "cell_type": "tielocell", - "name": ["sky130_fd_sc_hd__conb_1"], - "output_ports": ["LO"] - }, - { - "cell_type": "endcap", - "name": ["sky130_fd_sc_hd__tap_1"] - }, - { - "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"] - } - ] -} \ No newline at end of file diff --git a/hammer/technology/sky130/extra/sky130-tech-gen-files/sites.json b/hammer/technology/sky130/extra/sky130-tech-gen-files/sites.json deleted file mode 100644 index e243250cb..000000000 --- a/hammer/technology/sky130/extra/sky130-tech-gen-files/sites.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "sites": [ - {"name": "unithd", "x": 0.46, "y": 2.72}, - {"name": "unithddbl", "x": 0.46, "y": 5.44} - ] -} \ No newline at end of file diff --git a/hammer/technology/sky130/extra/sky130-tech-gen-files/stackups-gen.py b/hammer/technology/sky130/extra/sky130-tech-gen-files/stackups-gen.py deleted file mode 100755 index b6abc3511..000000000 --- a/hammer/technology/sky130/extra/sky130-tech-gen-files/stackups-gen.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python3 -# type: ignore -# tell mypy to ignore this file during typechecking -# -*- coding: utf-8 -*- -# -# Generate Hammer Sky130 tech plugin file: sky130.tech.json -# -# See LICENSE for licence details. -import sys -import json -import os - -library='sky130_fd_sc_hd' - -def main(args) -> int: - if len(args) != 3: - print("Usage: ./stackups-gen.py /path/to/sky130A stackups.json") - return 1 - - SKY130A = sys.argv[1] - - stackup = {} - stackup["name"] = library - stackup["grid_unit"] = 0.001 - stackup["metals"] = [] - - def is_float(string): - try: - float(string) - return True - except ValueError: - return False - - def get_min_from_line(line): - words = line.split() - nums = [float(w) for w in words if is_float(w)] - return min(nums) - - - tlef_path = os.path.join(SKY130A, 'libs.ref', library, 'techlef', f"{library}__min.tlef") - with open(tlef_path, 'r') as f: - metal_name = None - metal_index = 0 - lines = f.readlines() - idx = -1 - while idx < len(lines): - idx += 1 - if idx == len(lines) - 1: break - line = lines[idx] - if '#' in line: line = line[:line.index('#')] - words = line.split() - if line.startswith('LAYER') and len(words) > 1: - if words[1].startswith('li') or words[1].startswith('met'): - metal_name = words[1] - metal_index += 1 - metal = {} - metal["name"] = metal_name - metal["index"] = metal_index - - if metal_name is not None: - line = line.strip() - if line.startswith("DIRECTION"): - metal["direction"] = words[1].lower() - if line.startswith("PITCH"): - metal["pitch"] = get_min_from_line(line) - if line.startswith("OFFSET"): - metal["offset"] = get_min_from_line(line) - if line.startswith("WIDTH"): - metal["min_width"] = get_min_from_line(line) - if line.startswith("SPACINGTABLE"): - metal["power_strap_widths_and_spacings"] = [] - while ';' not in line: - idx += 1 - if idx == len(lines) - 1: break - line = lines[idx].strip() - if '#' in line: line = line[:line.index('#')] - words = line.split() - d = {} - if line.startswith("WIDTH"): - d["width_at_least"] = float(words[1]) - d["min_spacing"] = float(words[2]) - metal["power_strap_widths_and_spacings"].append(d.copy()) - if line.startswith("END"): - metal["grid_unit"] = 0.001 - stackup["metals"].append(metal.copy()) - metal_name = None - - - with open(sys.argv[2], 'w') as f: - json.dump(stackup, f, indent=2) - - return 0 - -if __name__ == '__main__': - sys.exit(main(sys.argv)) diff --git a/hammer/technology/sky130/extra/sky130-tech-gen-files/stackups.json b/hammer/technology/sky130/extra/sky130-tech-gen-files/stackups.json deleted file mode 100644 index 576f61446..000000000 --- a/hammer/technology/sky130/extra/sky130-tech-gen-files/stackups.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "name": "sky130_fd_sc_hd", - "grid_unit": 0.001, - "metals": [ - { - "name": "li1", - "index": 1, - "direction": "vertical", - "pitch": 0.34, - "offset": 0.17, - "min_width": 0.17, - "power_strap_widths_and_spacings": [ - { - "width_at_least": 0.0, - "min_spacing": 0.17 - } - ], - "grid_unit": 0.001 - }, - { - "name": "met1", - "index": 2, - "direction": "horizontal", - "pitch": 0.34, - "offset": 0.17, - "min_width": 0.14, - "power_strap_widths_and_spacings": [ - { - "width_at_least": 0.0, - "min_spacing": 0.14 - }, - { - "width_at_least": 3.0, - "min_spacing": 0.28 - } - ], - "grid_unit": 0.001 - }, - { - "name": "met2", - "index": 3, - "direction": "vertical", - "pitch": 0.46, - "offset": 0.23, - "min_width": 0.14, - "power_strap_widths_and_spacings": [ - { - "width_at_least": 0.0, - "min_spacing": 0.14 - }, - { - "width_at_least": 3.0, - "min_spacing": 0.28 - } - ], - "grid_unit": 0.001 - }, - { - "name": "met3", - "index": 4, - "direction": "horizontal", - "pitch": 0.68, - "offset": 0.34, - "min_width": 0.3, - "power_strap_widths_and_spacings": [ - { - "width_at_least": 0.0, - "min_spacing": 0.3 - }, - { - "width_at_least": 3.0, - "min_spacing": 0.4 - } - ], - "grid_unit": 0.001 - }, - { - "name": "met4", - "index": 5, - "direction": "vertical", - "pitch": 0.92, - "offset": 0.46, - "min_width": 0.3, - "power_strap_widths_and_spacings": [ - { - "width_at_least": 0.0, - "min_spacing": 0.3 - }, - { - "width_at_least": 3.0, - "min_spacing": 0.4 - } - ], - "grid_unit": 0.001 - }, - { - "name": "met5", - "index": 6, - "direction": "horizontal", - "pitch": 3.4, - "offset": 1.7, - "min_width": 1.6, - "power_strap_widths_and_spacings": [ - { - "width_at_least": 0.0, - "min_spacing": 1.6 - } - ], - "grid_unit": 0.001 - } - ] -} \ No newline at end of file diff --git a/hammer/technology/sky130/extra/sky130-tech-gen.py b/hammer/technology/sky130/extra/sky130-tech-gen.py deleted file mode 100755 index 19ddfe8ee..000000000 --- a/hammer/technology/sky130/extra/sky130-tech-gen.py +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env python3 -# type: ignore -# tell mypy to ignore this file during typechecking -# -*- coding: utf-8 -*- -# -# Generate Hammer Sky130 tech plugin file: sky130.tech.json -# -# See LICENSE for licence details. -import sys -import json -import os -import re -import functools - -use_nda_files=True - -def main(args) -> int: - if len(args) != 3: - print("Usage: ./sky130-tech-gen.py /path/to/sky130A sky130.tech.json") - return 1 - - SKY130A = sys.argv[1] - - if use_nda_files: - with open('sky130-tech-gen-files/beginning_nda.json', 'r') as f: data = json.load(f) - else: - with open('sky130-tech-gen-files/beginning.json', 'r') as f: data = json.load(f) - - with open('sky130-tech-gen-files/cells.json', 'r') as f: - cells = json.load(f) - data["physical_only_cells_list"] = cells["physical_only_cells_list"] - data["dont_use_list"] = cells["dont_use_list"] - data["special_cells"] = cells["special_cells"] - - # Standard cells - library='sky130_fd_sc_hd' - - SKYWATER_LIBS = os.path.join('$SKY130A', 'libs.ref', library) - LIBRARY_PATH = os.path.join( SKY130A, 'libs.ref', library, 'lib') - lib_corner_files=os.listdir(LIBRARY_PATH) - lib_corner_files.sort() - for cornerfilename in lib_corner_files: - if (not (library in cornerfilename) ) : continue - if ('ccsnoise' in cornerfilename): continue # ignore duplicate corner.lib/corner_ccsnoise.lib files - - tmp = cornerfilename.replace('.lib','') - if (tmp+'_ccsnoise.lib' in lib_corner_files): - cornerfilename=tmp+'_ccsnoise.lib' # use ccsnoise version of lib file - - cornername = tmp.split('__')[1] - cornerparts = cornername.split('_') - - speed = cornerparts[0] - if (speed == 'ff'): speed = 'fast' - if (speed == 'tt'): speed = 'typical' - if (speed == 'ss'): speed = 'slow' - - temp = cornerparts[1] - temp = temp.replace('n','-') - temp = temp.split('C')[0]+' C' - - vdd = cornerparts[2] - vdd = vdd.split('v')[0]+'.'+vdd.split('v')[1]+' V' - - lib_entry = { - "nldm_liberty_file": os.path.join(SKYWATER_LIBS,'lib', cornerfilename), - "verilog_sim": os.path.join('cache', library+'.v'), - "lef_file": os.path.join(SKYWATER_LIBS,'lef', library+'.lef'), - "spice_file": os.path.join('cache', library+'.cdl'), - "gds_file": os.path.join(SKYWATER_LIBS,'gds', library+'.gds'), - "corner": { - "nmos": speed, - "pmos": speed, - "temperature": temp - }, - "supplies": { - "VDD": vdd, - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - } - - data["libraries"].append(lib_entry) - - # IO cells - library='sky130_fd_io' - SKYWATER_LIBS = os.path.join('$SKY130A', 'libs.ref', library) - LIBRARY_PATH = os.path.join( SKY130A, 'libs.ref', library, 'lib') - lib_corner_files=os.listdir(LIBRARY_PATH) - lib_corner_files.sort() - for cornerfilename in lib_corner_files: - # Skip versions with no internal power - if ('nointpwr' in cornerfilename) : continue - - tmp = cornerfilename.replace('.lib','') - # Split into cell, and corner strings - # Resulting list if only one ff/ss/tt in name: [, , , , ] - # Resulting list if ff_ff/ss_ss/tt_tt in name: [, , , , '', , , , ] - split_cell_corner = re.split('_(ff)|_(ss)|_(tt)', tmp) - cell_name = split_cell_corner[0] - process = split_cell_corner[1:-1] - temp_volt = split_cell_corner[-1].split('_')[1:] - - # Filter out cross corners (e.g ff_ss or ss_ff) - if len(process) > 3: - if not functools.reduce(lambda x,y: x and y, map(lambda p,q: p==q, process[0:3], process[4:]), True): - continue - # Determine actual corner - speed = next(c for c in process if c is not None).replace('_','') - if (speed == 'ff'): speed = 'fast' - if (speed == 'tt'): speed = 'typical' - if (speed == 'ss'): speed = 'slow' - - temp = temp_volt[0] - temp = temp.replace('n','-') - temp = temp.split('C')[0]+' C' - - vdd = ('.').join(temp_volt[1].split('v')) + ' V' - # Filter out IO/analog voltages that are not high voltage - if temp_volt[2].startswith('1'): continue - if len(temp_volt) == 4: - if temp_volt[3].startswith('1'): continue - - # gpiov2_pad_wrapped has separate GDS - if cell_name == 'sky130_ef_io__gpiov2_pad_wrapped': - file_lib = 'sky130_ef_io' - gds_file = cell_name + '.gds' - lef_file = 'cache/sky130_ef_io.lef' - spice_file = os.path.join(SKYWATER_LIBS,'cdl', file_lib + '.cdl') - elif 'sky130_ef_io' in cell_name: - file_lib = 'sky130_ef_io' - gds_file = file_lib + '.gds' - lef_file = 'cache/' + file_lib + '.lef' - spice_file = os.path.join(SKYWATER_LIBS,'cdl', file_lib + '.cdl') - else: - file_lib = library - gds_file = file_lib + '.gds' - lef_file = os.path.join(SKYWATER_LIBS,'lef', file_lib + '.lef') - spice_file = os.path.join(SKYWATER_LIBS,'spice', file_lib + '.spice') - - lib_entry = { - "nldm_liberty_file": os.path.join(SKYWATER_LIBS,'lib', cornerfilename), - "verilog_sim": os.path.join(SKYWATER_LIBS,'verilog', file_lib + '.v'), - "lef_file": lef_file, - "spice_file": spice_file, - "gds_file": os.path.join(SKYWATER_LIBS,'gds', gds_file), - "corner": { - "nmos": speed, - "pmos": speed, - "temperature": temp - }, - "supplies": { - "VDD": vdd, - "GND": "0 V" - }, - "provides": [ - { - "lib_type": cell_name, - "vt": "RVT" - } - ] - } - - data["libraries"].append(lib_entry) - - with open('sky130-tech-gen-files/stackups.json', 'r') as f: - stackups = json.load(f) - data["stackups"] = [stackups] - - with open('sky130-tech-gen-files/sites.json', 'r') as f: - sites = json.load(f) - data["sites"] = sites["sites"] - - with open(sys.argv[2], 'w') as f: - json.dump(data, f, indent=2) - - return 0 - -if __name__ == '__main__': - sys.exit(main(sys.argv)) diff --git a/hammer/vlsi/driver.py b/hammer/vlsi/driver.py index 8b2862bdd..9a97c53da 100644 --- a/hammer/vlsi/driver.py +++ b/hammer/vlsi/driver.py @@ -65,7 +65,7 @@ def __init__(self, options: HammerDriverOptions, extra_project_config: dict = {} file_logger = HammerVLSIFileLogger(options.log_file) HammerVLSILogging.add_callback(file_logger.callback) self.log = HammerVLSILogging.context() # type: HammerVLSILoggingContext - + # Create a new hammer database. self.database = hammer_config.HammerDatabase() # type: hammer_config.HammerDatabase @@ -152,17 +152,15 @@ def load_technology(self, cache_dir: str = "") -> None: cache_dir = os.path.join(self.obj_dir, "tech-%s-cache" % tech_name) self.log.info("Loading technology '{0}'".format(tech_module)) - tech_opt = hammer_tech.HammerTechnology.load_from_module(tech_module) - if tech_opt is None: - self.log.fatal("Technology {0} not found or missing .tech.[json/yml]!".format(tech_module)) - return - else: - tech: hammer_tech.HammerTechnology = tech_opt - # Update database as soon as possible since e.g. extract_technology_files could use those settings + tech = hammer_tech.HammerTechnology.load_from_module(tech_module) self.database.update_technology(*tech.get_config()) tech.logger = self.log.context("tech") tech.set_database(self.database) tech.cache_dir = cache_dir + tech.gen_config() + if tech.config is None: + self.log.fatal("Technology {0} config not generated or missing .tech.[json/yml]!".format(tech_module)) + return tech.extract_technology_files() tech.get_lib_units() From 4206c3b6a3c0092862bce81063a3a49d65833ed8 Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Thu, 7 Mar 2024 19:38:09 -0800 Subject: [PATCH 02/68] remove original tech JSON --- hammer/technology/sky130/sky130.tech.json | 2058 --------------------- 1 file changed, 2058 deletions(-) delete mode 100644 hammer/technology/sky130/sky130.tech.json diff --git a/hammer/technology/sky130/sky130.tech.json b/hammer/technology/sky130/sky130.tech.json deleted file mode 100644 index 2d6d2d2bf..000000000 --- a/hammer/technology/sky130/sky130.tech.json +++ /dev/null @@ -1,2058 +0,0 @@ -{ - "name": "Skywater 130nm Library", - "grid_unit": "0.001", - "installs": [ - { - "id": "$SKY130_NDA", - "path": "technology.sky130.sky130_nda" - }, - { - "id": "$SKY130A", - "path": "technology.sky130.sky130A" - }, - { - "id": "$SKY130_CDS", - "path": "technology.sky130.sky130_cds" - } - ], - "layer_map_file": "$SKY130_NDA/s8/V2.0.1/VirtuosoOA/libs/technology_library/technology_library.layermap", - "drc_decks": [ - { - "tool_name": "calibre", - "deck_name": "calibre_drc", - "path": "$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules" - }, - { - "tool_name": "klayout", - "deck_name": "klayout_drc", - "path": "$SKY130A/libs.tech/klayout/drc/sky130A.lydrc" - }, - { - "tool_name": "pegasus", - "deck_name": "pegasus_drc", - "path": "$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl" - } - ], - "additional_drc_text": "", - "lvs_decks": [ - { - "tool_name": "calibre", - "deck_name": "calibre_lvs", - "old_path": "$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8", - "path": "cache/lvsControlFile_s8" - }, - { - "tool_name": "pegasus", - "deck_name": "pegasus_lvs", - "path": "$SKY130_CDS/Sky130_LVS/Sky130_rev_0.0_0.1.lvs.pvl" - } - ], - "additional_lvs_text": "", - "libraries": [ - { - "lef_file": "cache/sky130_fd_sc_hd__nom.tlef", - "verilog_sim": "cache/primitives.v", - "provides": [ - { - "lib_type": "technology" - } - ] - }, - { - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "provides": [ - { - "lib_type": "technology" - } - ] - }, - { - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io__analog.spice", - "provides": [ - { - "lib_type": "technology" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ff_100C_1v65.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.65 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ff_100C_1v95.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ff_n40C_1v56.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.56 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ff_n40C_1v65.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.65 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ff_n40C_1v76.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.76 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ff_n40C_1v95_ccsnoise.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ss_100C_1v40.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.40 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ss_100C_1v60.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ss_n40C_1v28.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.28 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ss_n40C_1v35.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.35 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ss_n40C_1v40.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.40 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ss_n40C_1v44.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.44 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ss_n40C_1v60_ccsnoise.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__ss_n40C_1v76.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.76 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__tt_025C_1v80.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__tt_100C_1v80.lib", - "verilog_sim": "cache/sky130_fd_sc_hd.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef", - "spice_file": "cache/sky130_fd_sc_hd.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_sc_hd/gds/sky130_fd_sc_hd.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "stdcell", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__gpiov2_pad_tt_tt_025C_1v80_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__gpiov2_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__gpiov2_pad_wrapped_ff_ff_100C_1v95_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io__gpiov2_pad_wrapped.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__gpiov2_pad_wrapped", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__gpiov2_pad_wrapped_ff_ff_n40C_1v95_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io__gpiov2_pad_wrapped.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__gpiov2_pad_wrapped", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__gpiov2_pad_wrapped_ss_ss_100C_1v60_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io__gpiov2_pad_wrapped.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__gpiov2_pad_wrapped", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__gpiov2_pad_wrapped_tt_tt_025C_1v80_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io__gpiov2_pad_wrapped.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__gpiov2_pad_wrapped", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vccd_lvc_clamped3_pad_ff_100C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vccd_lvc_clamped3_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vccd_lvc_clamped3_pad_ff_n40C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vccd_lvc_clamped3_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vccd_lvc_clamped3_pad_ss_100C_1v60_3v00_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vccd_lvc_clamped3_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vccd_lvc_clamped3_pad_tt_025C_1v80_3v30_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vccd_lvc_clamped3_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vccd_lvc_clamped_pad_ff_100C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vccd_lvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vccd_lvc_clamped_pad_ff_n40C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vccd_lvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vccd_lvc_clamped_pad_ss_100C_1v60_3v00_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vccd_lvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vccd_lvc_clamped_pad_tt_025C_1v80_3v30_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vccd_lvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vdda_hvc_clamped_pad_ff_100C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vdda_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vdda_hvc_clamped_pad_ff_n40C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vdda_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vdda_hvc_clamped_pad_ss_100C_1v60_3v00_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vdda_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vdda_hvc_clamped_pad_tt_025C_1v80_3v30_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vdda_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vddio_hvc_clamped_pad_ff_100C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vddio_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vddio_hvc_clamped_pad_ff_n40C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vddio_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vddio_hvc_clamped_pad_ss_100C_1v60_3v00_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vddio_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vddio_hvc_clamped_pad_tt_025C_1v80_3v30_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vddio_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssa_hvc_clamped_pad_ff_100C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssa_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssa_hvc_clamped_pad_ff_n40C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssa_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssa_hvc_clamped_pad_ss_100C_1v60_3v00_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssa_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssa_hvc_clamped_pad_tt_025C_1v80_3v30_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssa_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssd_lvc_clamped3_pad_ff_100C_1v95_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssd_lvc_clamped3_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssd_lvc_clamped3_pad_ff_n40C_1v95_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssd_lvc_clamped3_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssd_lvc_clamped3_pad_ss_100C_1v60_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssd_lvc_clamped3_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssd_lvc_clamped3_pad_tt_025C_1v80_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssd_lvc_clamped3_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssd_lvc_clamped3_pad_tt_100C_1v80_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssd_lvc_clamped3_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssd_lvc_clamped_pad_ff_100C_1v95_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssd_lvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssd_lvc_clamped_pad_ff_n40C_1v95_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssd_lvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssd_lvc_clamped_pad_ss_100C_1v60_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssd_lvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssd_lvc_clamped_pad_tt_025C_1v80_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssd_lvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssd_lvc_clamped_pad_tt_100C_1v80_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssd_lvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssio_hvc_clamped_pad_ff_100C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssio_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssio_hvc_clamped_pad_ff_n40C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssio_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssio_hvc_clamped_pad_ss_100C_1v60_3v00_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssio_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_ef_io__vssio_hvc_clamped_pad_tt_025C_1v80_3v30_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_ef_io.v", - "lef_file": "cache/sky130_ef_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/cdl/sky130_ef_io.cdl", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_ef_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_ef_io__vssio_hvc_clamped_pad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_gpiov2_ff_ff_100C_1v95_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_gpiov2", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_gpiov2_ff_ff_n40C_1v95_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_gpiov2", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_gpiov2_ss_ss_100C_1v60_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_gpiov2", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_gpiov2_tt_tt_025C_1v80_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_gpiov2", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_ground_hvc_wpad_ff_100C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_ground_hvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_ground_hvc_wpad_ff_n40C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_ground_hvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_ground_hvc_wpad_ss_100C_1v60_3v00_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_ground_hvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_ground_hvc_wpad_tt_025C_1v80_3v30_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_ground_hvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_ground_lvc_wpad_ff_100C_1v95_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_ground_lvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_ground_lvc_wpad_ff_n40C_1v95_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_ground_lvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_ground_lvc_wpad_ss_100C_1v60_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_ground_lvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_ground_lvc_wpad_tt_025C_1v80_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_ground_lvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_ground_lvc_wpad_tt_100C_1v80_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_ground_lvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_power_hvc_wpad_ff_100C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_power_hvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_power_hvc_wpad_ff_n40C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_power_hvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_power_hvc_wpad_ss_100C_1v60_3v00_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_power_hvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_power_hvc_wpad_tt_025C_1v80_3v30_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_power_hvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_power_lvc_wpad_ff_100C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_power_lvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_power_lvc_wpad_ff_n40C_1v95_5v50_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_power_lvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_power_lvc_wpad_ss_100C_1v60_3v00_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_power_lvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_power_lvc_wpad_tt_025C_1v80_3v30_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_power_lvc_wpad", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_xres4v2_ff_ff_100C_1v95_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_xres4v2", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_xres4v2_ff_ff_n40C_1v95_5v50.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "fast", - "pmos": "fast", - "temperature": "-40 C" - }, - "supplies": { - "VDD": "1.95 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_xres4v2", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_xres4v2_ss_ss_100C_1v60_3v00.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "slow", - "pmos": "slow", - "temperature": "100 C" - }, - "supplies": { - "VDD": "1.60 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_xres4v2", - "vt": "RVT" - } - ] - }, - { - "nldm_liberty_file": "$SKY130A/libs.ref/sky130_fd_io/lib/sky130_fd_io__top_xres4v2_tt_tt_025C_1v80_3v30.lib", - "verilog_sim": "$SKY130A/libs.ref/sky130_fd_io/verilog/sky130_fd_io.v", - "lef_file": "$SKY130A/libs.ref/sky130_fd_io/lef/sky130_fd_io.lef", - "spice_file": "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_fd_io.spice", - "gds_file": "$SKY130A/libs.ref/sky130_fd_io/gds/sky130_fd_io.gds", - "corner": { - "nmos": "typical", - "pmos": "typical", - "temperature": "025 C" - }, - "supplies": { - "VDD": "1.80 V", - "GND": "0 V" - }, - "provides": [ - { - "lib_type": "sky130_fd_io__top_xres4v2", - "vt": "RVT" - } - ] - } - ], - "physical_only_cells_list": [ - "sky130_fd_sc_hd__tap_1", - "sky130_fd_sc_hd__tap_2", - "sky130_fd_sc_hd__tapvgnd_1", - "sky130_fd_sc_hd__tapvgnd2_1", - "sky130_fd_sc_hd__tapvgnd_1", - "sky130_fd_sc_hd__tapvpwrvgnd_1", - "sky130_fd_sc_hd__fill_1", - "sky130_fd_sc_hd__fill_2", - "sky130_fd_sc_hd__fill_4", - "sky130_fd_sc_hd__fill_8", - "sky130_fd_sc_hd__diode_2", - "sky130_ef_io__corner_pad" - ], - "dont_use_list": [ - "*sdf*", - "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": "tiehicell", - "name": [ - "sky130_fd_sc_hd__conb_1" - ], - "output_ports": [ - "HI" - ] - }, - { - "cell_type": "tielocell", - "name": [ - "sky130_fd_sc_hd__conb_1" - ], - "output_ports": [ - "LO" - ] - }, - { - "cell_type": "endcap", - "name": [ - "sky130_fd_sc_hd__tap_1" - ] - }, - { - "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": [ - { - "name": "sky130_fd_sc_hd", - "grid_unit": 0.001, - "metals": [ - { - "name": "li1", - "index": 1, - "direction": "vertical", - "pitch": 0.34, - "offset": 0.17, - "min_width": 0.17, - "power_strap_widths_and_spacings": [ - { - "width_at_least": 0.0, - "min_spacing": 0.17 - } - ], - "grid_unit": 0.001 - }, - { - "name": "met1", - "index": 2, - "direction": "horizontal", - "pitch": 0.34, - "offset": 0.17, - "min_width": 0.14, - "power_strap_widths_and_spacings": [ - { - "width_at_least": 0.0, - "min_spacing": 0.14 - }, - { - "width_at_least": 3.0, - "min_spacing": 0.28 - } - ], - "grid_unit": 0.001 - }, - { - "name": "met2", - "index": 3, - "direction": "vertical", - "pitch": 0.46, - "offset": 0.23, - "min_width": 0.14, - "power_strap_widths_and_spacings": [ - { - "width_at_least": 0.0, - "min_spacing": 0.14 - }, - { - "width_at_least": 3.0, - "min_spacing": 0.28 - } - ], - "grid_unit": 0.001 - }, - { - "name": "met3", - "index": 4, - "direction": "horizontal", - "pitch": 0.68, - "offset": 0.34, - "min_width": 0.3, - "power_strap_widths_and_spacings": [ - { - "width_at_least": 0.0, - "min_spacing": 0.3 - }, - { - "width_at_least": 3.0, - "min_spacing": 0.4 - } - ], - "grid_unit": 0.001 - }, - { - "name": "met4", - "index": 5, - "direction": "vertical", - "pitch": 0.92, - "offset": 0.46, - "min_width": 0.3, - "power_strap_widths_and_spacings": [ - { - "width_at_least": 0.0, - "min_spacing": 0.3 - }, - { - "width_at_least": 3.0, - "min_spacing": 0.4 - } - ], - "grid_unit": 0.001 - }, - { - "name": "met5", - "index": 6, - "direction": "horizontal", - "pitch": 3.4, - "offset": 1.7, - "min_width": 1.6, - "power_strap_widths_and_spacings": [ - { - "width_at_least": 0.0, - "min_spacing": 1.6 - } - ], - "grid_unit": 0.001 - } - ] - } - ], - "sites": [ - { - "name": "unithd", - "x": 0.46, - "y": 2.72 - }, - { - "name": "unithddbl", - "x": 0.46, - "y": 5.44 - } - ] -} From e59a9b562144d8501fd9ffcc43b411fb8ffa6ab2 Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Thu, 7 Mar 2024 21:14:32 -0800 Subject: [PATCH 03/68] Move tech LEF metals parsing to LEFUtils (a bit clunky), fix type checker errors --- hammer/par/openroad/__init__.py | 5 +- hammer/technology/sky130/__init__.py | 88 ++++------------------------ hammer/utils/lef_utils.py | 72 ++++++++++++++++++++++- 3 files changed, 84 insertions(+), 81 deletions(-) diff --git a/hammer/par/openroad/__init__.py b/hammer/par/openroad/__init__.py index e19bad778..0a15dfec1 100644 --- a/hammer/par/openroad/__init__.py +++ b/hammer/par/openroad/__init__.py @@ -1450,10 +1450,7 @@ def write_gds(self) -> str: layer_map_file=self.get_gds_map_file() if layer_map_file is None: - self.logger.error("Must have GDS layer map for macro PG library generation! Skipping.") - return True - else: - assert isinstance(layer_map_file, str) + raise FileNotFoundError(f"Must have GDS layer map for write_gds! KLayout won't be able to write GDS file. ({layer_map_file}") # the first entry of $KLAYOUT_PATH will be the one where the configuration is stored when KLayout exits # otherwise KLayout tries to write everything to the same directory as the klayout binary and throws an error if it is not writeable diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 71a40c9e8..c7c4911ab 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -11,16 +11,10 @@ import json import functools -#import hammer.tech -from hammer.config import HammerDatabase from hammer.tech import * -from hammer.tech.stackup import * -from hammer.tech.specialcells import * from hammer.vlsi import HammerTool, HammerPlaceAndRouteTool, TCLTool, HammerDRCTool, HammerLVSTool, \ HammerToolHookAction, HierarchicalMode - -import hammer.tech.specialcells as specialcells -from hammer.tech.specialcells import CellType, SpecialCell +from hammer.utils import LEFUtils class SKY130Tech(HammerTechnology): """ @@ -134,15 +128,15 @@ def gen_config(self) -> None: "sky130_fd_sc_hd__probec_p_*" ] spcl_cells = [ - SpecialCell(cell_type = "tiehilocell", name = ["sky130_fd_sc_hd__conb_1"]), - SpecialCell(cell_type = "tiehicell", name = ["sky130_fd_sc_hd__conb_1"], output_ports = ["HI"]), - SpecialCell(cell_type = "tielocell", name = ["sky130_fd_sc_hd__conb_1"], output_ports = ["LO"]), - SpecialCell(cell_type = "endcap", name = ["sky130_fd_sc_hd__tap_1"]), - SpecialCell(cell_type = "tapcell", name = ["sky130_fd_sc_hd__tapvpwrvgnd_1"]), - SpecialCell(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"]), - SpecialCell(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"]), - SpecialCell(cell_type = "driver", name = ["sky130_fd_sc_hd__buf_4"], input_ports = ["A"], output_ports = ["X"]), - SpecialCell(cell_type = "ctsbuffer", name = ["sky130_fd_sc_hd__clkbuf_1"]) + SpecialCell(cell_type = CellType("tiehilocell"), name = ["sky130_fd_sc_hd__conb_1"]), + SpecialCell(cell_type = CellType("tiehicell"), name = ["sky130_fd_sc_hd__conb_1"], output_ports = ["HI"]), + SpecialCell(cell_type = CellType("tielocell"), name = ["sky130_fd_sc_hd__conb_1"], output_ports = ["LO"]), + SpecialCell(cell_type = CellType("endcap"), name = ["sky130_fd_sc_hd__tap_1"]), + SpecialCell(cell_type = CellType("tapcell"), name = ["sky130_fd_sc_hd__tapvpwrvgnd_1"]), + SpecialCell(cell_type = CellType("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"]), + SpecialCell(cell_type = CellType("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"]), + SpecialCell(cell_type = CellType("driver"), name = ["sky130_fd_sc_hd__buf_4"], input_ports = ["A"], output_ports = ["X"]), + SpecialCell(cell_type = CellType("ctsbuffer"), name = ["sky130_fd_sc_hd__clkbuf_1"]) ] # Generate standard cell library @@ -200,67 +194,9 @@ def gen_config(self) -> None: libs.append(lib_entry) # Generate stackup - metals = [] #type: List[Metal] - def is_float(string): - try: - float(string) - return True - except ValueError: - return False - - def get_min_from_line(line): - words = line.split() - nums = [float(w) for w in words if is_float(w)] - return min(nums) - tlef_path = os.path.join(SKY130A, 'libs.ref', library, 'techlef', f"{library}__min.tlef") - with open(tlef_path, 'r') as f: - metal_name = None - metal_index = 0 - lines = f.readlines() - idx = -1 - while idx < len(lines): - idx += 1 - if idx == len(lines) - 1: break - line = lines[idx] - if '#' in line: line = line[:line.index('#')] - words = line.split() - if line.startswith('LAYER') and len(words) > 1: - if words[1].startswith('li') or words[1].startswith('met'): - metal_name = words[1] - metal_index += 1 - metal = {} - metal["name"] = metal_name - metal["index"] = metal_index - - if metal_name is not None: - line = line.strip() - if line.startswith("DIRECTION"): - metal["direction"] = words[1].lower() - if line.startswith("PITCH"): - metal["pitch"] = get_min_from_line(line) - if line.startswith("OFFSET"): - metal["offset"] = get_min_from_line(line) - if line.startswith("WIDTH"): - metal["min_width"] = get_min_from_line(line) - if line.startswith("SPACINGTABLE"): - metal["power_strap_widths_and_spacings"] = [] - while ';' not in line: - idx += 1 - if idx == len(lines) - 1: break - line = lines[idx].strip() - if '#' in line: line = line[:line.index('#')] - words = line.split() - d = {} - if line.startswith("WIDTH"): - d["width_at_least"] = float(words[1]) - d["min_spacing"] = float(words[2]) - metal["power_strap_widths_and_spacings"].append(d.copy()) - if line.startswith("END"): - metal["grid_unit"] = 0.001 - metals.append(Metal.model_validate(metal.copy())) - metal_name = None - stackups.append(Stackup(name = slib, grid_unit = Decimal("0.001"), metals = metals)) + metals = list(map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path))) + stackups.append(Stackup(name = slib, grid_unit = Decimal("0.001"), metals = metals)) elif slib == "sky130_scl": raise NotImplementedError(f"sky130_scl not yet supported") diff --git a/hammer/utils/lef_utils.py b/hammer/utils/lef_utils.py index da526ad98..b74f56a68 100644 --- a/hammer/utils/lef_utils.py +++ b/hammer/utils/lef_utils.py @@ -6,7 +6,7 @@ import re from decimal import Decimal -from typing import List, Optional, Tuple +from typing import List, Optional, Tuple, Dict, Union __all__ = ['LEFUtils'] @@ -92,3 +92,73 @@ def get_sizes(source: str) -> List[Tuple[str, Decimal, Decimal]]: raise ValueError("Unexpected end of file in MACRO block {m}".format(m=in_macro)) return output + + @staticmethod + def get_metals(source: str) -> List[Dict]: + """ + Parse a tech LEF to extract Metal fields. + Note: list(map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path))) + is required to convert this into a list of Metals (we can't import stackups classes here) + """ + metals = [] + def is_float(string): + try: + float(string) + return True + except ValueError: + return False + + def get_min_from_line(line): + words = line.split() + nums = [float(w) for w in words if is_float(w)] + return min(nums) + + with open(source, 'r') as f: + metal_name = None + metal_index = 0 + lines = f.readlines() + idx = -1 + while idx < len(lines): + idx += 1 + if idx == len(lines) - 1: break + line = lines[idx] + if '#' in line: line = line[:line.index('#')] + words = line.split() + if line.startswith('LAYER') and len(words) > 1: + if words[1].startswith('li') or words[1].startswith('met'): + metal_name = words[1] + metal_index += 1 + metal = {} + metal["name"] = metal_name + metal["index"] = metal_index # type: ignore + + if metal_name is not None: + line = line.strip() + if line.startswith("DIRECTION"): + metal["direction"] = words[1].lower() + if line.startswith("PITCH"): + metal["pitch"] = get_min_from_line(line) + if line.startswith("OFFSET"): + metal["offset"] = get_min_from_line(line) + if line.startswith("WIDTH"): + metal["min_width"] = get_min_from_line(line) + if line.startswith("SPACINGTABLE"): + metal["power_strap_widths_and_spacings"] = [] # type: ignore + while ';' not in line: + idx += 1 + if idx == len(lines) - 1: break + line = lines[idx].strip() + if '#' in line: line = line[:line.index('#')] + words = line.split() + d = {} + if line.startswith("WIDTH"): + d["width_at_least"] = float(words[1]) + d["min_spacing"] = float(words[2]) + metal["power_strap_widths_and_spacings"].append(d.copy()) # type: ignore + # TODO: need power_strap_width_table + if line.startswith("END"): + metal["grid_unit"] = 0.001 # type: ignore + metals.append(metal.copy()) + metal_name = None + + return metals From 87de6efa533358ae1d9edf08f8bfa8763a4e09b4 Mon Sep 17 00:00:00 2001 From: Elam Day-Friedland Date: Thu, 7 Mar 2024 21:22:17 -0800 Subject: [PATCH 04/68] working through fixing paths for sky130 cds --- hammer/technology/sky130/__init__.py | 102 ++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 71a40c9e8..a83dfa48d 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -21,6 +21,8 @@ import hammer.tech.specialcells as specialcells from hammer.tech.specialcells import CellType, SpecialCell +from hammer.utils import LEFUtils + class SKY130Tech(HammerTechnology): """ @@ -31,6 +33,7 @@ def gen_config(self) -> None: """Generate the tech config, based on the library type selected""" slib = self.get_setting("technology.sky130.stdcell_library") SKY130A = self.get_setting("technology.sky130.sky130A") + SKY130_CDS = self.get_setting("technology.sky130.sky130_cds") # Common tech LEF and IO cell spice netlists libs = [ @@ -262,8 +265,102 @@ def get_min_from_line(line): metal_name = None stackups.append(Stackup(name = slib, grid_unit = Decimal("0.001"), metals = metals)) - elif slib == "sky130_scl": - raise NotImplementedError(f"sky130_scl not yet supported") + elif slib == "sky130_scl_9T_0.0.2": #sky130_scl": + base_slib = "sky130_scl" + phys_only = [ + "sky130_fd_sc_hd__tap_1", "sky130_fd_sc_hd__tap_2", "sky130_fd_sc_hd__tapvgnd_1", "sky130_fd_sc_hd__tapvpwrvgnd_1", + "sky130_fd_sc_hd__fill_1", "sky130_fd_sc_hd__fill_2", "sky130_fd_sc_hd__fill_4", "sky130_fd_sc_hd__fill_8", + "sky130_fd_sc_hd__diode_2"] + dont_use = [ + "*sdf*", + "sky130_fd_sc_hd__probe_p_*", + "sky130_fd_sc_hd__probec_p_*" + ] + spcl_cells = [ + SpecialCell(cell_type = "tiehilocell", name = ["sky130_fd_sc_hd__conb_1"]), + SpecialCell(cell_type = "tiehicell", name = ["sky130_fd_sc_hd__conb_1"], output_ports = ["HI"]), + SpecialCell(cell_type = "tielocell", name = ["sky130_fd_sc_hd__conb_1"], output_ports = ["LO"]), + SpecialCell(cell_type = "endcap", name = ["sky130_fd_sc_hd__tap_1"]), + SpecialCell(cell_type = "tapcell", name = ["sky130_fd_sc_hd__tapvpwrvgnd_1"]), + SpecialCell(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"]), + SpecialCell(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"]), + SpecialCell(cell_type = "driver", name = ["sky130_fd_sc_hd__buf_4"], input_ports = ["A"], output_ports = ["X"]), + SpecialCell(cell_type = "ctsbuffer", name = ["sky130_fd_sc_hd__clkbuf_1"]) + ] + + # Generate standard cell library + library=slib + + LIBRARY_PATH = os.path.join( SKY130_CDS, 'LIB', slib) + lib_corner_files=os.listdir(LIBRARY_PATH) + lib_corner_files.sort() + for cornerfilename in lib_corner_files: + if (not (library in cornerfilename) ) : continue + if ('ccsnoise' in cornerfilename): continue # ignore duplicate corner.lib/corner_ccsnoise.lib files + + tmp = cornerfilename.replace('.lib','') + if (tmp+'_ccsnoise.lib' in lib_corner_files): + cornerfilename=tmp+'_ccsnoise.lib' # use ccsnoise version of lib file + + cornername = tmp.split('__')[1] + cornerparts = cornername.split('_') + + speed = cornerparts[0] + if (speed == 'ff'): speed = 'fast' + if (speed == 'tt'): speed = 'typical' + if (speed == 'ss'): speed = 'slow' + + temp = cornerparts[1] + temp = temp.replace('n','-') + temp = temp.split('C')[0]+' C' + + vdd = cornerparts[2] + vdd = vdd.split('v')[0]+'.'+vdd.split('v')[1]+' V' + + lib_entry = Library( + nldm_liberty_file = os.path.join(SKY130_CDS,'lib', cornerfilename), + verilog_sim = os.path.join('cache', library+'.v'), + lef_file = os.path.join(SKY130_CDS,'lef', library+'.lef'), + spice_file = os.path.join('cache', library+'.cdl'), + gds_file = os.path.join(SKY130_CDS,'gds', library+'.gds'), + corner = Corner( + nmos = speed, + pmos = speed, + temperature = temp + ), + supplies = Supplies( + VDD = vdd, + GND = "0 V" + ), + provides = [Provide( + lib_type = "stdcell", + vt = "RVT" + ) + ] + ) + + libs.append(lib_entry) + + # Generate stackup + metals = [] #type: List[Metal] + def is_float(string): + try: + float(string) + return True + except ValueError: + return False + + def get_min_from_line(line): + words = line.split() + nums = [float(w) for w in words if is_float(w)] + return min(nums) + + # TODO elam is 9t ok to hard code + tlef_path = os.path.join(SKY130_CDS, 'LIB', library, 'lef', f"{base_slib}_9T.tlef") + metals = list(map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path))) + stackups.append(Stackup(name = slib, grid_unit = Decimal("0.001"), metals = metals)) + + else: raise ValueError(f"Incorrect standard cell library selection: {slib}") @@ -379,6 +476,7 @@ def setup_verilog(self) -> None: sl = sf.readlines() # Find timing declaration + import pdb; pdb.set_trace() start_idx = [idx for idx, line in enumerate(sl) if "`ifndef SKY130_FD_SC_HD__LPFLOW_BLEEDER_1_TIMING_V" in line][0] # Search for the broken statement From d4916a323f31688dff489a9b88605135de3db9c7 Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Thu, 7 Mar 2024 21:58:43 -0800 Subject: [PATCH 05/68] no abstractmethod, remove sky130-specific things in tech LEF parsing --- hammer/tech/__init__.py | 3 +-- hammer/utils/lef_utils.py | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/hammer/tech/__init__.py b/hammer/tech/__init__.py index f93d0a2b0..73c711db8 100644 --- a/hammer/tech/__init__.py +++ b/hammer/tech/__init__.py @@ -353,9 +353,8 @@ def __init__(self): self.time_unit: Optional[str] = None self.cap_unit: Optional[str] = None - @abstractmethod def gen_config(self) -> None: - """For subclasses to set self.config directly, instead of from static JSON file""" + """For subclasses to set self.config (type: TechJSON) directly, instead of from static JSON file""" pass @classmethod diff --git a/hammer/utils/lef_utils.py b/hammer/utils/lef_utils.py index b74f56a68..259f99522 100644 --- a/hammer/utils/lef_utils.py +++ b/hammer/utils/lef_utils.py @@ -125,7 +125,7 @@ def get_min_from_line(line): if '#' in line: line = line[:line.index('#')] words = line.split() if line.startswith('LAYER') and len(words) > 1: - if words[1].startswith('li') or words[1].startswith('met'): + if lines[idx+1].strip().startswith('TYPE ROUTING'): metal_name = words[1] metal_index += 1 metal = {} @@ -155,8 +155,20 @@ def get_min_from_line(line): d["width_at_least"] = float(words[1]) d["min_spacing"] = float(words[2]) metal["power_strap_widths_and_spacings"].append(d.copy()) # type: ignore - # TODO: need power_strap_width_table + #width table is a bit more complex + metal["power_strap_width_table"] = [] # type: ignore + # definition on one line + if "WIDTHTABLE" in line: + # definition on one line + if "LEF58_WIDTHTABLE" in line: + metal["power_strap_width_table"] = list(filter(lambda s: is_float(s), line.split())) + # multiple tables, only want routing direction one + if not any(s in line for x in ["ORTHOGONAL", "WRONGDIRECTION"]): + metal["power_strap_width_table"] = list(filter(lambda s: is_float(s), line.split())) + if line.startswith("END"): + # TODO: grid_unit is not currently parsed as part of the Metal data structure! + # See #379 metal["grid_unit"] = 0.001 # type: ignore metals.append(metal.copy()) metal_name = None From 73223f81a9ca2f28de481306ab441434736dfffc Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Thu, 7 Mar 2024 22:02:41 -0800 Subject: [PATCH 06/68] typo, type checker --- hammer/utils/lef_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hammer/utils/lef_utils.py b/hammer/utils/lef_utils.py index 259f99522..7b4436ea7 100644 --- a/hammer/utils/lef_utils.py +++ b/hammer/utils/lef_utils.py @@ -161,10 +161,10 @@ def get_min_from_line(line): if "WIDTHTABLE" in line: # definition on one line if "LEF58_WIDTHTABLE" in line: - metal["power_strap_width_table"] = list(filter(lambda s: is_float(s), line.split())) + metal["power_strap_width_table"] = list(filter(lambda s: is_float(s), line.split())) # type: ignore # multiple tables, only want routing direction one - if not any(s in line for x in ["ORTHOGONAL", "WRONGDIRECTION"]): - metal["power_strap_width_table"] = list(filter(lambda s: is_float(s), line.split())) + if not any(s in line for s in ["ORTHOGONAL", "WRONGDIRECTION"]): + metal["power_strap_width_table"] = list(filter(lambda s: is_float(s), line.split())) # type: ignore if line.startswith("END"): # TODO: grid_unit is not currently parsed as part of the Metal data structure! From d58c30ecce66827ff7ec78819b38fb386646c5e0 Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Thu, 7 Mar 2024 23:43:27 -0800 Subject: [PATCH 07/68] Add sky130_scl key, documentation, and cleanup --- doc/Technology/Tech-class.rst | 1 + doc/Technology/Tech-json.rst | 205 ++++++++++++++++++-- hammer/tech/__init__.py | 9 +- hammer/technology/sky130/README.md | 16 ++ hammer/technology/sky130/defaults.yml | 3 + hammer/technology/sky130/defaults_types.yml | 4 + 6 files changed, 213 insertions(+), 25 deletions(-) diff --git a/doc/Technology/Tech-class.rst b/doc/Technology/Tech-class.rst index e71d8be47..58bc778aa 100644 --- a/doc/Technology/Tech-class.rst +++ b/doc/Technology/Tech-class.rst @@ -28,6 +28,7 @@ HammerTechnology Class The HammerTechnology class is the base class that all technology plugins should inherit from and is defined in ``hammer/tech/__init__.py``. Particularly useful methods are: +* ``gen_config``: the plugin subclass should override this method to generate the tech config. See the next section (:ref:`tech-json`) for details. * ``post_install_script``: the plugin subclass should override this method to apply any non-default PDK installation steps or hotfixes in order to set up the technology libraries for use with Hammer. * ``read_libs``: this is a particularly powerful method to read the libraries contained in the ``tech.json`` file and filter them using the filters also contained the same file. See the :ref:`filters` section for details. * ``get_tech__hooks``: these methods are necessary if tech--specific hooks are needed, and must return a list of hooks when a given tool name implementing the relevant action is called. Refer to the :ref:`hooks` section for details about how to write hooks. diff --git a/doc/Technology/Tech-json.rst b/doc/Technology/Tech-json.rst index ef4a412bc..29dd8048d 100644 --- a/doc/Technology/Tech-json.rst +++ b/doc/Technology/Tech-json.rst @@ -1,16 +1,51 @@ .. _tech-json: -Hammer Tech JSON +Hammer Tech Config (Tech JSON) =============================== -The ``tech.json`` for a given technology sets up some general information about the install of the PDK, sets up DRC rule decks, sets up pointers to PDK files, and supplies technology stackup information. -For the full schema of the tech JSON, please see the :ref:`full_schema` section below, which is derived from the ``TechJSON`` Pydantic BaseModel in ``hammer/tech/__init__.py``. +Technology plugins must set up some general information about the install of the PDK, set up DRC rule decks, set up pointers to PDK files, and supply technology stackup information. +Formerly, this information was provided in a static ``.tech.json`` file. We now encourage you to generate this information in the form of ``TechJSON`` Pydantic BaseModel instances (see ``hammer/tech/__init__.py`` for all BaseModel definitions). Instructions are given below for both forms, with examples from the Sky130 and ASAP7 plugins. + +For the full schema of the tech configuration, please see the :ref:`full_schema` section below, which is derived from ``TechJSON``. Technology Install --------------------------------- -The user may supply the PDK to Hammer as an already extracted directory and/or as a tarball that Hammer can automatically extract. Setting ``technology.TECH_NAME.`` ``install_dir`` and/or ``tarball_dir`` (key is setup in the defaults.yml) will fill in as the path prefix for paths supplied to PDK files in the rest of the ``tech.json``. -Below is an example of the installs and tarballs from the ASAP7 plugin. +The user may supply the PDK to Hammer as an already extracted directory and/or as a tarball that Hammer can automatically extract. Setting ``technology.TECH_NAME.`` key (as defined in the plugin's ``defaults.yml``) will fill in as the path prefix for paths supplied to PDK files. + +Path prefixes can be supplied in multiple forms. The options are as follows (taken from ``prepend_dir_path`` in ``hammer/tech/__init__py``): + +#. Absolute path: the path starts with "/" and refers to an absolute path on the filesystem + * ``/path/to/a/lib/file.lib`` -> ``/path/to/a/lib/file.lib`` +#. Tech plugin relative path: the path has no "/"s and refers to a file directly inside the tech plugin folder + * ``techlib.lib`` -> ``/techlib.lib`` +#. Tech cache relative path: the path starts with an identifier which is "cache" (this is used in the SKY130 example below) + * ``cache/primitives.v`` -> ``/primitives.v`` +#. Install relative path: the path starts with an install/tarball identifier (installs.id, tarballs.root.id) and refers to a file relative to that identifier's path + * ``pdkroot/dac/dac.lib`` -> ``/nfs/ecad/tsmc100/stdcells/dac/dac.lib`` +#. Library extra_prefix path: the path starts with an identifier present in the provided library's ``extra_prefixes`` Field + * ``lib1/cap150f.lib`` -> ``/design_files/caps/cap150f.lib`` + +Below is an example of the installs and tarballs defining path prefixes from the Sky130 and ASAP7 plugins. + +Pydantic: + +.. code-block:: python + + def gen_config(self) -> None: + #... + self.config = TechJSON( + name = "Skywater 130nm Library", + grid_unit = "0.001", + installs = [ + PathPrefix(id = "$SKY130_NDA", path = "technology.sky130.sky130_nda"), + PathPrefix(id = "$SKY130A", path = "technology.sky130.sky130A"), + PathPrefix(id = "$SKY130_CDS", path = "technology.sky130.sky130_cds") + ], + #fields skipped... + ) + +JSON: .. code-block:: json @@ -37,13 +72,33 @@ Below is an example of the installs and tarballs from the ASAP7 plugin. } ], -The ``id`` field is used within the file listings further down in the file to prefix ``path``, as shown in detail below. If the file listing begins with ``cache``, then this denotes files that exist in the tech cache, which are generally placed there by the tech plugin's post-installation script (see ASAP7's ``post_install_script`` method). Finally, the encrypted Calibre decks are provided in a tarball and denoted as optional. +The ``id`` field is used within the file listings further down in the file to prefix ``path``, as shown in detail below. If the file listing begins with ``cache``, then this denotes files that exist in the tech cache, which are generally placed there by the tech plugin's post-installation script (see ASAP7's ``post_install_script`` method). In the ASAP7 example, the encrypted Calibre decks are provided in a tarball and denoted as optional. DRC/LVS Deck Setup --------------------------------- As many DRC & LVS decks for as many tools can be specified in the ``drc decks`` and ``lvs decks`` keys. Additional DRC/LVS commands can be appended to the generated run files by specifying raw text in the ``additional_drc_text`` and ``additional_lvs_text`` keys. -Below is an example of an LVS deck from the ASAP7 plugin. + +Pydantic: + +.. code-block:: python + + def gen_config(self) -> None: + #... + self.config = TechJSON( + #fields skipped... + drc_decks = [ + DRCDeck(tool_name = "calibre", deck_name = "calibre_drc", path = "$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules"), + DRCDeck(tool_name = "klayout", deck_name = "klayout_drc", path = "$SKY130A/libs.tech/klayout/drc/sky130A.lydrc"), + DRCDeck(tool_name = "pegasus", deck_name = "pegasus_drc", path = "$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl") + ], + additional_drc_text = "", + #fields skipped... + ) + +The example above contains decks for 3 different tools, with file pointers using the installs prefixes defined before. + +JSON: .. code-block:: json @@ -61,8 +116,59 @@ The file pointers, in this case, use the tarball prefix because Hammer will be e Library Setup --------------------------------- -The ``libraries`` key also must be setup in the JSON plugin. This will tell Hammer where to find all of the relevant files for standard cells and other blocks for the VLSI flow. -Below is an example of the start of the library setup and one entry from the ASAP7 plugin. +The ``libraries`` Field also must be set in the TechJSON instance. This will tell Hammer where to find all of the relevant files for standard cells and other blocks for the VLSI flow. Path prefixes are used most heavily here. + +The ``corner`` Field (BaseModel type: Corner) tells Hammer what process and temperature corner that these files correspond to. The ``supplies`` Field (BaseModel type: Supplies) tells Hammer what the nominal supply for these cells are. +The ``provides`` Field (type: List[Provide]) has several sub-fields that tell Hammer what kind of library this is (examples include ``stdcell``, ``fiducials``, ``io pad cells``, ``bump``, and ``level shifters``) and the threshold voltage flavor of the cells, if applicable. +Adding the tech LEF for the technology with the ``lib_type`` set as ``technology`` is necessary for place and route. This must be the first ``lef_file`` provided in the entire list of Libraries. + +Pydantic: + +.. code-block:: python + + def gen_config(self) -> None: + #... + libs = [ + Library(lef_file = "cache/sky130_fd_sc_hd__nom.tlef", verilog_sim = "cache/primitives.v", provides = [Provide(lib_type = "technology")]), + Library(spice_file = "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io__analog.spice", provides = [Provide(lib_type = "IO library")]) + ] + #... + #Generate loops + SKYWATER_LIBS = os.path.join('$SKY130A', 'libs.ref', library) + for cornerfilename in lib_corner_files: + #... + lib_entry = Library( + nldm_liberty_file = os.path.join(SKYWATER_LIBS,'lib', cornerfilename), + verilog_sim = os.path.join(SKYWATER_LIBS,'verilog', file_lib + '.v'), + lef_file = lef_file, + spice_file = spice_file, + gds_file = os.path.join(SKYWATER_LIBS,'gds', gds_file), + corner = Corner( + nmos = speed, + pmos = speed, + temperature = temp + ), + supplies = Supplies( + VDD = vdd, + GND ="0 V" + ), + provides = [Provide( + lib_type = cell_name, + vt = "RVT" + ) + ] + ) + libs.append(lib_entry) + #... + self.config = TechJSON( + #fields skipped... + libraries = libs, + #fields skipped... + ) + +In the above example, we use the ``$SKY130A`` prefix and some loops to generate Library entries. These loops are often derived from the directory structure of the standard cell library. + +JSON: .. code-block:: json @@ -102,9 +208,7 @@ Below is an example of the start of the library setup and one entry from the ASA ] }, -The file pointers, in this case, use the ``$PDK`` and ``$STDCELLS`` prefix as defined in the installs. The ``corner`` key tells Hammer what process and temperature corner that these files correspond to. The ``supplies`` key tells Hammer what the nominal supply for these cells are. -The ``provides`` key has several sub-keys that tell Hammer what kind of library this is (examples include ``stdcell``, ``fiducials``, ``io pad cells``, ``bump``, and ``level shifters``) and the threshold voltage flavor of the cells, if applicable. -Adding the tech LEF for the technology with the ``lib_type`` set as ``technology`` is necessary for place and route. +The file pointers, in this case, use the ``$PDK`` and ``$STDCELLS`` prefix as defined in the installs. .. _filters: @@ -117,32 +221,79 @@ For a list of pre-built library filters, refer to the properties in the ``Librar Stackup -------------------------------- -The ``stackups`` sets up the important metal layer information for Hammer to use. -Below is an example of one metal layer in the ``metals`` list from the ASAP7 example tech plugin. +The ``stackups`` sets up the important metal layer information for Hammer to use. All this information is typically taken from the tech LEF and can be automatically filled in with a script. + +You can use ``LEFUtils.get_metals`` to generate the stackup information for simple tech LEFs: + +.. code-block:: python + + from hammer.tech import * + from hammer.utils import LEFUtils + class SKY130Tech(HammerTechnology): + def gen_config(self) -> None: + #... + stackups = [] # type: List[Stackup] + tlef_path = os.path.join(SKY130A, 'libs.ref', library, 'techlef', f"{library}__min.tlef") + metals = list(map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path))) + stackups.append(Stackup(name = library, grid_unit = Decimal("0.001"), metals = metals)) + + self.config = TechJSON( + #fields skipped... + stackups = stackups, + #fields skipped... + ) + + +Below is an example of one metal layer in the ``metals`` list from the ASAP7 example tech plugin. This gives a better idea of the serialized fields in the Metal BaseModel. This is extracted by loading the tech LEF into Innovus, then using the ``hammer/par/innovus/dump_stackup_to_json.tcl`` script. .. code-block:: json {"name": "M3", "index": 3, "direction": "vertical", "min_width": 0.072, "pitch": 0.144, "offset": 0.0, "power_strap_widths_and_spacings": [{"width_at_least": 0.0, "min_spacing": 0.072}], "power_strap_width_table": [0.072, 0.36, 0.648, 0.936, 1.224, 1.512]} -All this information is typically taken from the tech LEF and can be automatically filled in with a script. The metal layer name and layer number is specified. ``direction`` specifies the preferred routing direction for the layer. ``min_width`` and ``pitch`` specify the minimum width wire and the track pitch, respectively. ``power_strap_widths_and_spacings`` is a list of pairs that specify design rules relating to the widths of wires and minimum required spacing between them. This information is used by Hammer when drawing power straps to make sure it is conforming to some basic design rules. +The metal layer name and layer number is specified. ``direction`` specifies the preferred routing direction for the layer. ``min_width`` and ``pitch`` specify the minimum width wire and the track pitch, respectively. ``power_strap_widths_and_spacings`` is a list of pairs that specify design rules relating to the widths of wires and minimum required spacing between them. This information is used by Hammer when drawing power straps to make sure it is conforming to some basic design rules. Sites -------------------------------- The ``sites`` field specifies the unit standard cell size of the technology for Hammer. +.. code-block:: python + + def gen_config(self) -> None: + #... + self.config = TechJSON( + #fields skipped... + sites = [ + Site(name = "unithd", x = Decimal("0.46"), y = Decimal("2.72")), + Site(name = "unithddbl", x = Decimal("0.46"), y = Decimal("5.44")) + ], + #fields skipped... + ) + .. code-block:: json "sites": [ {"name": "asap7sc7p5t", "x": 0.216, "y": 1.08} ] -This is an example from the ASAP7 tech plugin in which the ``name`` parameter specifies the core site name used in the tech LEF, and the ``x`` and ``y`` parameters specify the width and height of the unit standard cell size, respectively. +These are examples from the Sky130 and ASAP7 tech plugin in which the ``name`` parameter specifies the core site name used in the tech LEF, and the ``x`` and ``y`` parameters specify the width and height of the unit standard cell size, respectively. Special Cells -------------------------------- The ``special_cells`` field specifies a set of cells in the technology that have special functions. -The example below shows a subset of the ASAP7 tech plugin for 2 types of cells: ``tapcell`` and ``stdfiller``. +The example below shows a subset of the Sky130 and ASAP7 tech plugin for 2 types of cells: ``tapcell`` and ``stdfiller``. + +.. code-block:: python + + def gen_config(self) -> None: + #... + self.config = TechJSON( + #fields skipped... + special_cells = [ + SpecialCell(cell_type = CellType("tapcell"), name = ["sky130_fd_sc_hd__tapvpwrvgnd_1"]), + SpecialCell(cell_type = CellType("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"]), + ] + ) .. code-block:: json @@ -161,7 +312,25 @@ There is an optional ``size`` list. For each element in its corresponding ``name Don't Use, Physical-Only Cells -------------------------------- The ``dont_use_list`` is used to denote cells that should be excluded due to things like bad timing models or layout. -The ``physical_only_cells_list`` is used to denote cells that contain only physical geometry, which means that they should be excluded from netlisting for simulation and LVS. Examples from the ASAP7 plugin are below: +The ``physical_only_cells_list`` is used to denote cells that contain only physical geometry, which means that they should be excluded from netlisting for simulation and LVS. Examples: + +.. code-block:: python + + def gen_config(self) -> None: + #... + self.config = TechJSON( + #fields skipped... + physical_only_cells_list = [ + "sky130_fd_sc_hd__tap_1", "sky130_fd_sc_hd__tap_2", "sky130_fd_sc_hd__tapvgnd_1", "sky130_fd_sc_hd__tapvpwrvgnd_1", + "sky130_fd_sc_hd__fill_1", "sky130_fd_sc_hd__fill_2", "sky130_fd_sc_hd__fill_4", "sky130_fd_sc_hd__fill_8", + "sky130_fd_sc_hd__diode_2"] + dont_use_list = [ + "*sdf*", + "sky130_fd_sc_hd__probe_p_*", + "sky130_fd_sc_hd__probec_p_*" + ] + #fields skipped... + ) .. code-block:: json diff --git a/hammer/tech/__init__.py b/hammer/tech/__init__.py index 73c711db8..25de611fa 100644 --- a/hammer/tech/__init__.py +++ b/hammer/tech/__init__.py @@ -358,7 +358,6 @@ def gen_config(self) -> None: pass @classmethod - #def load_from_module(cls, tech_module: str) -> Optional["HammerTechnology"]: def load_from_module(cls, tech_module: str) -> "HammerTechnology": """Load a technology from a given module. @@ -371,13 +370,9 @@ def load_from_module(cls, tech_module: str) -> "HammerTechnology": tech.name = technology_name tech.package = tech_module - #tech.config = tech.gen_config() tech_json = importlib.resources.files(tech_module) / f"{technology_name}.tech.json" tech_yaml = importlib.resources.files(tech_module) / f"{technology_name}.tech.yml" - #if tech.config is not None: # pydantic model already created - # print("Tech gen") - # return tech if tech_json.is_file(): tech.config = TechJSON.model_validate_json(tech_json.read_text()) return tech @@ -659,13 +654,13 @@ def prepend_dir_path(self, path: str, lib: Optional[Library] = None) -> str: /path/to/a/lib/file.lib -> /path/to/a/lib/file.lib 2. Tech plugin relative path: the path has no "/"s and refers to a file directly inside the tech plugin folder techlib.lib -> /techlib.lib - 3. Tech cache relative path: the path starts with an identifier which is "cache" (this is used in the SKY130 tech JSON) + 3. Tech cache relative path: the path starts with an identifier which is "cache" (this is used in the SKY130 Libraries) cache/primitives.v -> /primitives.v 4. Install relative path: the path starts with an install/tarball identifier (installs.id, tarballs.root.id) and refers to a file relative to that identifier's path pdkroot/dac/dac.lib -> /nfs/ecad/tsmc100/stdcells/dac/dac.lib 5. Library extra_prefix path: the path starts with an identifier present in the provided - library's extra_prefixes + library's extra_prefixes Field lib1/cap150f.lib -> /design_files/caps/cap150f.lib """ assert len(path) > 0, "path must not be empty" diff --git a/hammer/technology/sky130/README.md b/hammer/technology/sky130/README.md index 9bfbcac28..2a630ad01 100644 --- a/hammer/technology/sky130/README.md +++ b/hammer/technology/sky130/README.md @@ -45,6 +45,22 @@ Now in your Hammer YAML configs, point to the location of this install: technology.sky130.sky130A: "/share/pdk/sky130A" ``` +### Cadence PDK + +Cadence has also created a PDK and standard cell library based on the original open PDK, and optimized for use with Cadence tools. Usage of this PDK is purely optional, but enables DRC/LVS with Pegasus, and IR drop analysis with Voltus. + +You can download them from [here](https://support.cadence.com/apex/ArticleAttachmentPortal?id=a1Od000000051TqEAI&pageName=ArticleContent) (Cadence Support account is required). After downloading and untar-ing the packages, point to the location of this install: + +```yaml +technology.sky130.sky130_cds: "" +technology.sky130.sky130_scl: "" +``` + +To select this standard cell library (instead of the default `sky130_fd_sc_hd`): + +```yaml +technology.sky130.stdcell_library: "sky130_scl" +``` SRAM Macros ----------- diff --git a/hammer/technology/sky130/defaults.yml b/hammer/technology/sky130/defaults.yml index 5de945ec9..b22fb09e8 100644 --- a/hammer/technology/sky130/defaults.yml +++ b/hammer/technology/sky130/defaults.yml @@ -11,6 +11,9 @@ technology.sky130: sky130_cds: "/path/to/sky130_cds" # This contains the Cadence PDK # this key is OPTIONAL, and is only required if you are running Pegasus DRC/LVS. + + sky130_scl: "/path/to/sky130_cdl" # This contains the Cadence standard cell library + # this key is OPTIONAL, and is only required if you want to do Voltus IR drop analysis. sram22_sky130_macros: "/path/to/sram22_sky130_macros" # SRAM path # /path/to/sram22_sky130_macros diff --git a/hammer/technology/sky130/defaults_types.yml b/hammer/technology/sky130/defaults_types.yml index c3510631e..a0196e2a9 100644 --- a/hammer/technology/sky130/defaults_types.yml +++ b/hammer/technology/sky130/defaults_types.yml @@ -15,6 +15,10 @@ technology.sky130: # this key is OPTIONAL, and is only required if you are running Pegasus DRC/LVS. sky130_cds: str + # This contains the Cadence standard cell library + # this key is OPTIONAL, and is only required if you want to do Voltus IR drop analysis. + sky130_scl: str + # RAM paths # OpenRAM openram_lib: str From 43e250672068595c8b8416c2230f9233aff45d4b Mon Sep 17 00:00:00 2001 From: Elam Day-Friedland Date: Fri, 8 Mar 2024 13:22:41 -0800 Subject: [PATCH 08/68] give offset default value --- hammer/utils/lef_utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hammer/utils/lef_utils.py b/hammer/utils/lef_utils.py index b74f56a68..fff2ad297 100644 --- a/hammer/utils/lef_utils.py +++ b/hammer/utils/lef_utils.py @@ -158,6 +158,9 @@ def get_min_from_line(line): # TODO: need power_strap_width_table if line.startswith("END"): metal["grid_unit"] = 0.001 # type: ignore + # Give 'offset' a default value to make pydantic happy + if 'offset' not in metal: + metal['offset'] = Decimal("0.0") metals.append(metal.copy()) metal_name = None From 56ad76d3b6336d15fd86f90ddb2548b1b85e581a Mon Sep 17 00:00:00 2001 From: Harrison Liew Date: Fri, 8 Mar 2024 15:20:15 -0800 Subject: [PATCH 09/68] move sky130_lefpin.map to plugin root, clarify prefixes documentation --- doc/Technology/Tech-json.rst | 2 +- hammer/tech/__init__.py | 2 +- hammer/technology/sky130/__init__.py | 2 +- hammer/technology/sky130/{extra => }/sky130_lefpin.map | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename hammer/technology/sky130/{extra => }/sky130_lefpin.map (100%) diff --git a/doc/Technology/Tech-json.rst b/doc/Technology/Tech-json.rst index 29dd8048d..2c5388906 100644 --- a/doc/Technology/Tech-json.rst +++ b/doc/Technology/Tech-json.rst @@ -17,7 +17,7 @@ Path prefixes can be supplied in multiple forms. The options are as follows (tak #. Absolute path: the path starts with "/" and refers to an absolute path on the filesystem * ``/path/to/a/lib/file.lib`` -> ``/path/to/a/lib/file.lib`` -#. Tech plugin relative path: the path has no "/"s and refers to a file directly inside the tech plugin folder +#. Tech plugin relative path: the path has no "/"s and refers to a file directly inside the tech plugin folder (no subdirectories allowed, else it conflicts with 3-5. below!) * ``techlib.lib`` -> ``/techlib.lib`` #. Tech cache relative path: the path starts with an identifier which is "cache" (this is used in the SKY130 example below) * ``cache/primitives.v`` -> ``/primitives.v`` diff --git a/hammer/tech/__init__.py b/hammer/tech/__init__.py index 25de611fa..cd40baa1a 100644 --- a/hammer/tech/__init__.py +++ b/hammer/tech/__init__.py @@ -652,7 +652,7 @@ def prepend_dir_path(self, path: str, lib: Optional[Library] = None) -> str: 1. Absolute path: the path starts with "/" and refers to an absolute path on the filesystem /path/to/a/lib/file.lib -> /path/to/a/lib/file.lib - 2. Tech plugin relative path: the path has no "/"s and refers to a file directly inside the tech plugin folder + 2. Tech plugin relative path: the path has no "/"s and refers to a file directly inside the tech plugin folder (no subdirectories allowed, else it conflicts with 3-5. below!) techlib.lib -> /techlib.lib 3. Tech cache relative path: the path starts with an identifier which is "cache" (this is used in the SKY130 Libraries) cache/primitives.v -> /primitives.v diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index c7c4911ab..27e94ae24 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -213,7 +213,7 @@ def gen_config(self) -> None: PathPrefix(id = "$SKY130_CDS", path = "technology.sky130.sky130_cds") ], libraries = libs, - gds_map_file = "extra/sky130_lefpin.map", + gds_map_file = "sky130_lefpin.map", physical_only_cells_list = phys_only, dont_use_list = dont_use, drc_decks = [ diff --git a/hammer/technology/sky130/extra/sky130_lefpin.map b/hammer/technology/sky130/sky130_lefpin.map similarity index 100% rename from hammer/technology/sky130/extra/sky130_lefpin.map rename to hammer/technology/sky130/sky130_lefpin.map From eb0bbc6ac59bc77d40d14f11f91ac5f87bfa54cd Mon Sep 17 00:00:00 2001 From: Elam Day-Friedland Date: Fri, 8 Mar 2024 15:32:55 -0800 Subject: [PATCH 10/68] update variable refs --- hammer/technology/sky130/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 015747a7d..3295bebb2 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -27,7 +27,7 @@ def gen_config(self) -> None: SKY130A = self.get_setting("technology.sky130.sky130A") SKY130_CDS = self.get_setting("technology.sky130.sky130_cds") # TODO elam propogate to examples that this should look like "sky130_cds/LIB/sky130_scl_9T_0.0.2" on bwrc - SKY130_CDS_LIB = self.get_setting("technology.sky130.sky130_cds_lib") + SKY130_CDS_LIB = self.get_setting("technology.sky130.sky130_scl") # Common tech LEF and IO cell spice netlists # TODO elam use different techlef based on stdcell lib @@ -308,7 +308,8 @@ def get_min_from_line(line): installs = [ PathPrefix(id = "$SKY130_NDA", path = "technology.sky130.sky130_nda"), PathPrefix(id = "$SKY130A", path = "technology.sky130.sky130A"), - PathPrefix(id = "$SKY130_CDS", path = "technology.sky130.sky130_cds") + PathPrefix(id = "$SKY130_CDS", path = "technology.sky130.sky130_cds"), + PathPrefix(id = "$SKY130_SCL", path = "technology.sky130.sky130_scl") ], libraries = libs, gds_map_file = "extra/sky130_lefpin.map", @@ -387,7 +388,7 @@ def setup_cdl(self) -> None: # (the open-source RTL sim tools don't treat undeclared signals as errors) # - Deal with numerous inconsistencies in timing specify blocks. def setup_verilog(self) -> None: - setting_dir = self.get_setting("technology.sky130.sky130_cds_lib") + setting_dir = self.get_setting("technology.sky130.sky130_scl") setting_dir = Path(setting_dir) slib = self.get_setting("technology.sky130.stdcell_library") From 67b17866b81f75c7d68c97b96e72c35bbd657dd7 Mon Sep 17 00:00:00 2001 From: Nayiri K Date: Fri, 8 Mar 2024 17:00:42 -0800 Subject: [PATCH 11/68] fixing Makefile error from spaces instead of tabs --- e2e/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/Makefile b/e2e/Makefile index 00c370bac..a66bb878c 100644 --- a/e2e/Makefile +++ b/e2e/Makefile @@ -45,4 +45,4 @@ $(HAMMER_D_MK): -include $(HAMMER_D_MK) clean: - rm -rf $(OBJ_DIR) hammer-vlsi-*.log + rm -rf $(OBJ_DIR) hammer-vlsi-*.log From 0a61cd9d6f74fb042c9ccdb66b8401c653d5acfb Mon Sep 17 00:00:00 2001 From: Nayiri K Date: Fri, 8 Mar 2024 17:01:22 -0800 Subject: [PATCH 12/68] we hack the calibre lvs deck in setup_calibre_lvs_deck so need to specify cache path --- hammer/technology/sky130/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 27e94ae24..b01317418 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -223,7 +223,7 @@ def gen_config(self) -> None: ], additional_drc_text = "", lvs_decks = [ - LVSDeck(tool_name = "calibre", deck_name = "calibre_lvs", path = "$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8"), + LVSDeck(tool_name = "calibre", deck_name = "calibre_lvs", path = "cache/lvsControlFile_s8"), LVSDeck(tool_name = "pegasus", deck_name = "pegasus_lvs", path = "$SKY130_CDS/Sky130_LVS/Sky130_rev_0.0_0.1.lvs.pvl") ], additional_lvs_text = "", From 5571f593f22decdbed03bdc5eb36700c840fce64 Mon Sep 17 00:00:00 2001 From: Nayiri K Date: Fri, 8 Mar 2024 17:02:08 -0800 Subject: [PATCH 13/68] merge lines separated by \ in libfiles --- hammer/utils/lib_utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hammer/utils/lib_utils.py b/hammer/utils/lib_utils.py index 582bf9875..65e706fb1 100644 --- a/hammer/utils/lib_utils.py +++ b/hammer/utils/lib_utils.py @@ -53,4 +53,6 @@ def get_headers(source: str) -> List[str]: # TODO: handle other compressed file types? Tools only seem to support gzip. fd = os.open(source, os.O_RDONLY) lines = os.pread(fd, nbytes, 0).splitlines() - return list(map(lambda l: l.decode('ascii', errors='ignore'), lines)) + lines = list(map(lambda l: l.decode('ascii', errors='ignore'), lines)) + all_lines = '\n'.join(lines).replace('\\\n','') # combine lines that are split by \ character + return all_lines.split('\n') From 2c18e6be4cfcf1869307285cfa4a31803aca0d40 Mon Sep 17 00:00:00 2001 From: Elam Day-Friedland Date: Sat, 9 Mar 2024 17:14:10 -0800 Subject: [PATCH 14/68] stuck on where the io lefs are in cadence pdk --- hammer/technology/sky130/__init__.py | 606 +++++++++++++++++---------- hammer/vlsi/units.py | 33 +- 2 files changed, 413 insertions(+), 226 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 6e5ea29db..dce4b5234 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -4,7 +4,8 @@ import sys import re -import os, shutil +import os +import shutil from pathlib import Path from typing import NamedTuple, List, Optional, Tuple, Dict, Set, Any import importlib @@ -16,11 +17,13 @@ HammerToolHookAction, HierarchicalMode from hammer.utils import LEFUtils + class SKY130Tech(HammerTechnology): """ Override the HammerTechnology used in `hammer_tech.py` This class is loaded by function `load_from_json`, and will pass the `try` in `importlib`. """ + def gen_config(self) -> None: """Generate the tech config, based on the library type selected""" slib = self.get_setting("technology.sky130.stdcell_library") @@ -31,22 +34,37 @@ def gen_config(self) -> None: # Common tech LEF and IO cell spice netlists # TODO elam use different techlef based on stdcell lib - libs = [ - Library(lef_file = "cache/sky130_fd_sc_hd__nom.tlef", verilog_sim = "cache/primitives.v", provides = [Provide(lib_type = "technology")]), - Library(spice_file = "$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io__analog.spice", provides = [Provide(lib_type = "IO library")]) - ] + libs = [] + if slib == "sky130_fd_sc_hd": + libs = [ + Library(lef_file="/sky130_fd_sc_hd__nom.tlef", + verilog_sim="cache/primitives.v", provides=[Provide(lib_type="technology")]), + Library(spice_file="$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io__analog.spice", + provides=[Provide(lib_type="IO library")]) + ] + elif slib == "sky130_scl": + libs = [ + Library(lef_file="$SKY130_SCL/lef/sky130_scl_9T.tlef", + verilog_sim="$SKY130_SCL/verilog/sky130_scl_9T.v", provides=[Provide(lib_type="technology")]), + # Library(spice_file="$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io__analog.spice", + # provides=[Provide(lib_type="IO library")]) + ] + else: + raise ValueError( + f"Incorrect standard cell library selection: {slib}") # Generate IO cells # TODO elam do this with cadence pdk? - library='sky130_fd_io' + library = 'sky130_fd_io' SKYWATER_LIBS = os.path.join('$SKY130A', 'libs.ref', library) - LIBRARY_PATH = os.path.join( SKY130A, 'libs.ref', library, 'lib') - lib_corner_files=os.listdir(LIBRARY_PATH) + LIBRARY_PATH = os.path.join(SKY130A, 'libs.ref', library, 'lib') + lib_corner_files = os.listdir(LIBRARY_PATH) lib_corner_files.sort() for cornerfilename in lib_corner_files: # Skip versions with no internal power - if ('nointpwr' in cornerfilename) : continue + if ('nointpwr' in cornerfilename): + continue - tmp = cornerfilename.replace('.lib','') + tmp = cornerfilename.replace('.lib', '') # Split into cell, and corner strings # Resulting list if only one ff/ss/tt in name: [, , , , ] # Resulting list if ff_ff/ss_ss/tt_tt in name: [, , , , '', , , , ] @@ -57,60 +75,71 @@ def gen_config(self) -> None: # Filter out cross corners (e.g ff_ss or ss_ff) if len(process) > 3: - if not functools.reduce(lambda x,y: x and y, map(lambda p,q: p==q, process[0:3], process[4:]), True): + if not functools.reduce(lambda x, y: x and y, map(lambda p, q: p == q, process[0:3], process[4:]), True): continue # Determine actual corner - speed = next(c for c in process if c is not None).replace('_','') - if (speed == 'ff'): speed = 'fast' - if (speed == 'tt'): speed = 'typical' - if (speed == 'ss'): speed = 'slow' + speed = next(c for c in process if c is not None).replace('_', '') + if (speed == 'ff'): + speed = 'fast' + if (speed == 'tt'): + speed = 'typical' + if (speed == 'ss'): + speed = 'slow' temp = temp_volt[0] - temp = temp.replace('n','-') + temp = temp.replace('n', '-') temp = temp.split('C')[0]+' C' vdd = ('.').join(temp_volt[1].split('v')) + ' V' # Filter out IO/analog voltages that are not high voltage - if temp_volt[2].startswith('1'): continue + if temp_volt[2].startswith('1'): + continue if len(temp_volt) == 4: - if temp_volt[3].startswith('1'): continue + if temp_volt[3].startswith('1'): + continue # gpiov2_pad_wrapped has separate GDS if cell_name == 'sky130_ef_io__gpiov2_pad_wrapped': file_lib = 'sky130_ef_io' gds_file = cell_name + '.gds' lef_file = 'cache/sky130_ef_io.lef' - spice_file = os.path.join(SKYWATER_LIBS,'cdl', file_lib + '.cdl') + spice_file = os.path.join( + SKYWATER_LIBS, 'cdl', file_lib + '.cdl') elif 'sky130_ef_io' in cell_name: file_lib = 'sky130_ef_io' gds_file = file_lib + '.gds' lef_file = 'cache/' + file_lib + '.lef' - spice_file = os.path.join(SKYWATER_LIBS,'cdl', file_lib + '.cdl') + spice_file = os.path.join( + SKYWATER_LIBS, 'cdl', file_lib + '.cdl') else: file_lib = library gds_file = file_lib + '.gds' - lef_file = os.path.join(SKYWATER_LIBS,'lef', file_lib + '.lef') - spice_file = os.path.join(SKYWATER_LIBS,'spice', file_lib + '.spice') + lef_file = os.path.join( + SKYWATER_LIBS, 'lef', file_lib + '.lef') + spice_file = os.path.join( + SKYWATER_LIBS, 'spice', file_lib + '.spice') lib_entry = Library( - nldm_liberty_file = os.path.join(SKYWATER_LIBS,'lib', cornerfilename), - verilog_sim = os.path.join(SKYWATER_LIBS,'verilog', file_lib + '.v'), - lef_file = lef_file, - spice_file = spice_file, - gds_file = os.path.join(SKYWATER_LIBS,'gds', gds_file), - corner = Corner( - nmos = speed, - pmos = speed, - temperature = temp + nldm_liberty_file=os.path.join( + SKYWATER_LIBS, 'lib', cornerfilename), + verilog_sim=os.path.join( + SKYWATER_LIBS, 'verilog', file_lib + '.v'), + lef_file=lef_file, + spice_file=spice_file, + gds_file=os.path.join(SKYWATER_LIBS, 'gds', gds_file), + corner=Corner( + nmos=speed, + pmos=speed, + temperature=temp ), - supplies = Supplies( - VDD = vdd, - GND ="0 V" + supplies=Supplies( + VDD=vdd, + GND="0 V" ), - provides = [Provide( - lib_type = cell_name, - vt = "RVT" - ) + provides=[Provide( + lib_type=cell_name, + vt="RVT" + ) ] ) libs.append(lib_entry) @@ -133,77 +162,99 @@ def gen_config(self) -> None: "sky130_fd_sc_hd__probec_p_*" ] spcl_cells = [ - SpecialCell(cell_type = CellType("tiehilocell"), name = ["sky130_fd_sc_hd__conb_1"]), - SpecialCell(cell_type = CellType("tiehicell"), name = ["sky130_fd_sc_hd__conb_1"], output_ports = ["HI"]), - SpecialCell(cell_type = CellType("tielocell"), name = ["sky130_fd_sc_hd__conb_1"], output_ports = ["LO"]), - SpecialCell(cell_type = CellType("endcap"), name = ["sky130_fd_sc_hd__tap_1"]), - SpecialCell(cell_type = CellType("tapcell"), name = ["sky130_fd_sc_hd__tapvpwrvgnd_1"]), - SpecialCell(cell_type = CellType("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"]), - SpecialCell(cell_type = CellType("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"]), - SpecialCell(cell_type = CellType("driver"), name = ["sky130_fd_sc_hd__buf_4"], input_ports = ["A"], output_ports = ["X"]), - SpecialCell(cell_type = CellType("ctsbuffer"), name = ["sky130_fd_sc_hd__clkbuf_1"]) + SpecialCell(cell_type=CellType("tiehilocell"), + name=["sky130_fd_sc_hd__conb_1"]), + SpecialCell(cell_type=CellType("tiehicell"), name=[ + "sky130_fd_sc_hd__conb_1"], output_ports=["HI"]), + SpecialCell(cell_type=CellType("tielocell"), name=[ + "sky130_fd_sc_hd__conb_1"], output_ports=["LO"]), + SpecialCell(cell_type=CellType("endcap"), + name=["sky130_fd_sc_hd__tap_1"]), + SpecialCell(cell_type=CellType("tapcell"), name=[ + "sky130_fd_sc_hd__tapvpwrvgnd_1"]), + SpecialCell(cell_type=CellType("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"]), + SpecialCell(cell_type=CellType("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"]), + SpecialCell(cell_type=CellType("driver"), name=[ + "sky130_fd_sc_hd__buf_4"], input_ports=["A"], output_ports=["X"]), + SpecialCell(cell_type=CellType("ctsbuffer"), + name=["sky130_fd_sc_hd__clkbuf_1"]) ] # Generate standard cell library - library=slib + library = slib SKYWATER_LIBS = os.path.join('$SKY130A', 'libs.ref', library) - LIBRARY_PATH = os.path.join( SKY130A, 'libs.ref', library, 'lib') - lib_corner_files=os.listdir(LIBRARY_PATH) + LIBRARY_PATH = os.path.join(SKY130A, 'libs.ref', library, 'lib') + lib_corner_files = os.listdir(LIBRARY_PATH) lib_corner_files.sort() for cornerfilename in lib_corner_files: - if (not (library in cornerfilename) ) : continue - if ('ccsnoise' in cornerfilename): continue # ignore duplicate corner.lib/corner_ccsnoise.lib files + if (not (library in cornerfilename)): + continue + if ('ccsnoise' in cornerfilename): + continue # ignore duplicate corner.lib/corner_ccsnoise.lib files - tmp = cornerfilename.replace('.lib','') + tmp = cornerfilename.replace('.lib', '') if (tmp+'_ccsnoise.lib' in lib_corner_files): - cornerfilename=tmp+'_ccsnoise.lib' # use ccsnoise version of lib file + cornerfilename = tmp+'_ccsnoise.lib' # use ccsnoise version of lib file cornername = tmp.split('__')[1] cornerparts = cornername.split('_') speed = cornerparts[0] - if (speed == 'ff'): speed = 'fast' - if (speed == 'tt'): speed = 'typical' - if (speed == 'ss'): speed = 'slow' + if (speed == 'ff'): + speed = 'fast' + if (speed == 'tt'): + speed = 'typical' + if (speed == 'ss'): + speed = 'slow' temp = cornerparts[1] - temp = temp.replace('n','-') + temp = temp.replace('n', '-') temp = temp.split('C')[0]+' C' vdd = cornerparts[2] vdd = vdd.split('v')[0]+'.'+vdd.split('v')[1]+' V' lib_entry = Library( - nldm_liberty_file = os.path.join(SKYWATER_LIBS,'lib', cornerfilename), - verilog_sim = os.path.join('cache', library+'.v'), - lef_file = os.path.join(SKYWATER_LIBS,'lef', library+'.lef'), - spice_file = os.path.join('cache', library+'.cdl'), - gds_file = os.path.join(SKYWATER_LIBS,'gds', library+'.gds'), - corner = Corner( - nmos = speed, - pmos = speed, - temperature = temp + nldm_liberty_file=os.path.join( + SKYWATER_LIBS, 'lib', cornerfilename), + verilog_sim=os.path.join( + 'cache', library+'.v'), + lef_file=os.path.join( + SKYWATER_LIBS, 'lef', library+'.lef'), + spice_file=os.path.join( + 'cache', library+'.cdl'), + gds_file=os.path.join( + SKYWATER_LIBS, 'gds', library+'.gds'), + corner=Corner( + nmos=speed, + pmos=speed, + temperature=temp ), - supplies = Supplies( - VDD = vdd, - GND = "0 V" + supplies=Supplies( + VDD=vdd, + GND="0 V" ), - provides = [Provide( - lib_type = "stdcell", - vt = "RVT" - ) + provides=[Provide( + lib_type="stdcell", + vt="RVT" + ) ] ) libs.append(lib_entry) # Generate stackup - tlef_path = os.path.join(SKY130A, 'libs.ref', library, 'techlef', f"{library}__min.tlef") - metals = list(map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path))) - stackups.append(Stackup(name = slib, grid_unit = Decimal("0.001"), metals = metals)) - - elif slib == "sky130_scl": #sky130_scl": + tlef_path = os.path.join( + SKY130A, 'libs.ref', library, 'techlef', f"{library}__min.tlef") + metals = list(map(lambda m: Metal.model_validate(m), + LEFUtils.get_metals(tlef_path))) + stackups.append( + Stackup(name=slib, grid_unit=Decimal("0.001"), metals=metals)) + + elif slib == "sky130_scl": phys_only = [ "sky130_fd_sc_hd__tap_1", "sky130_fd_sc_hd__tap_2", "sky130_fd_sc_hd__tapvgnd_1", "sky130_fd_sc_hd__tapvpwrvgnd_1", "sky130_fd_sc_hd__fill_1", "sky130_fd_sc_hd__fill_2", "sky130_fd_sc_hd__fill_4", "sky130_fd_sc_hd__fill_8", @@ -214,72 +265,93 @@ def gen_config(self) -> None: "sky130_fd_sc_hd__probec_p_*" ] spcl_cells = [ - SpecialCell(cell_type = "tiehilocell", name = ["sky130_fd_sc_hd__conb_1"]), - SpecialCell(cell_type = "tiehicell", name = ["sky130_fd_sc_hd__conb_1"], output_ports = ["HI"]), - SpecialCell(cell_type = "tielocell", name = ["sky130_fd_sc_hd__conb_1"], output_ports = ["LO"]), - SpecialCell(cell_type = "endcap", name = ["sky130_fd_sc_hd__tap_1"]), - SpecialCell(cell_type = "tapcell", name = ["sky130_fd_sc_hd__tapvpwrvgnd_1"]), - SpecialCell(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"]), - SpecialCell(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"]), - SpecialCell(cell_type = "driver", name = ["sky130_fd_sc_hd__buf_4"], input_ports = ["A"], output_ports = ["X"]), - SpecialCell(cell_type = "ctsbuffer", name = ["sky130_fd_sc_hd__clkbuf_1"]) + SpecialCell(cell_type="tiehilocell", name=[ + "sky130_fd_sc_hd__conb_1"]), + SpecialCell(cell_type="tiehicell", name=[ + "sky130_fd_sc_hd__conb_1"], output_ports=["HI"]), + SpecialCell(cell_type="tielocell", name=[ + "sky130_fd_sc_hd__conb_1"], output_ports=["LO"]), + SpecialCell(cell_type="endcap", name=[ + "sky130_fd_sc_hd__tap_1"]), + SpecialCell(cell_type="tapcell", name=[ + "sky130_fd_sc_hd__tapvpwrvgnd_1"]), + SpecialCell(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"]), + SpecialCell(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"]), + SpecialCell(cell_type="driver", name=[ + "sky130_fd_sc_hd__buf_4"], input_ports=["A"], output_ports=["X"]), + # TODO elam port the above ones too + # SpecialCell(cell_type = "ctsbuffer", name = ["sky130_fd_sc_hd__clkbuf_1"]) + SpecialCell(cell_type="ctsbuffer", name=["CLKBUFX2"]) ] # Generate standard cell library - library=slib + library = slib - LIBRARY_PATH = SKY130_CDS_LIB - lib_corner_files=os.listdir(LIBRARY_PATH) + LIBRARY_PATH = SKY130_CDS_LIB + lib_corner_files = os.listdir(LIBRARY_PATH) lib_corner_files.sort() for cornerfilename in lib_corner_files: - if (not (library in cornerfilename) ) : continue - if ('ccsnoise' in cornerfilename): continue # ignore duplicate corner.lib/corner_ccsnoise.lib files + if (not (library in cornerfilename)): + continue + if ('ccsnoise' in cornerfilename): + continue # ignore duplicate corner.lib/corner_ccsnoise.lib files - tmp = cornerfilename.replace('.lib','') + tmp = cornerfilename.replace('.lib', '') if (tmp+'_ccsnoise.lib' in lib_corner_files): - cornerfilename=tmp+'_ccsnoise.lib' # use ccsnoise version of lib file + cornerfilename = tmp+'_ccsnoise.lib' # use ccsnoise version of lib file cornername = tmp.split('__')[1] cornerparts = cornername.split('_') speed = cornerparts[0] - if (speed == 'ff'): speed = 'fast' - if (speed == 'tt'): speed = 'typical' - if (speed == 'ss'): speed = 'slow' + if (speed == 'ff'): + speed = 'fast' + if (speed == 'tt'): + speed = 'typical' + if (speed == 'ss'): + speed = 'slow' temp = cornerparts[1] - temp = temp.replace('n','-') + temp = temp.replace('n', '-') temp = temp.split('C')[0]+' C' vdd = cornerparts[2] vdd = vdd.split('v')[0]+'.'+vdd.split('v')[1]+' V' + import pdb + pdb.set_trace() lib_entry = Library( - nldm_liberty_file = os.path.join(SKY130_CDS,'lib', cornerfilename), - verilog_sim = os.path.join('cache', library+'.v'), - lef_file = os.path.join(SKY130_CDS,'lef', library+'.lef'), - spice_file = os.path.join('cache', library+'.cdl'), - gds_file = os.path.join(SKY130_CDS,'gds', library+'.gds'), - corner = Corner( - nmos = speed, - pmos = speed, - temperature = temp + nldm_liberty_file=os.path.join( + SKY130_CDS, 'lib', cornerfilename), + verilog_sim=os.path.join( + 'cache', library+'.v'), + lef_file=os.path.join(SKY130_CDS, 'lef', library+'.lef'), + spice_file=os.path.join( + 'cache', library+'.cdl'), + gds_file=os.path.join(SKY130_CDS, 'gds', library+'.gds'), + corner=Corner( + nmos=speed, + pmos=speed, + temperature=temp ), - supplies = Supplies( - VDD = vdd, - GND = "0 V" + supplies=Supplies( + VDD=vdd, + GND="0 V" ), - provides = [Provide( - lib_type = "stdcell", - vt = "RVT" - ) + provides=[Provide( + lib_type="stdcell", + vt="RVT" + ) ] ) libs.append(lib_entry) # Generate stackup - metals = [] #type: List[Metal] + metals = [] # type: List[Metal] + def is_float(string): try: float(string) @@ -294,52 +366,63 @@ def get_min_from_line(line): # TODO elam is 9t ok to hard code tlef_path = os.path.join(SKY130_CDS_LIB, 'lef', f"{slib}_9T.tlef") - metals = list(map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path))) - stackups.append(Stackup(name = slib, grid_unit = Decimal("0.001"), metals = metals)) - + metals = list(map(lambda m: Metal.model_validate(m), + LEFUtils.get_metals(tlef_path))) + stackups.append( + Stackup(name=slib, grid_unit=Decimal("0.001"), metals=metals)) else: - raise ValueError(f"Incorrect standard cell library selection: {slib}") + raise ValueError( + f"Incorrect standard cell library selection: {slib}") self.config = TechJSON( - name = "Skywater 130nm Library", - grid_unit = "0.001", - shrink_factor = None, - installs = [ - PathPrefix(id = "$SKY130_NDA", path = "technology.sky130.sky130_nda"), - PathPrefix(id = "$SKY130A", path = "technology.sky130.sky130A"), - PathPrefix(id = "$SKY130_CDS", path = "technology.sky130.sky130_cds"), - PathPrefix(id = "$SKY130_SCL", path = "technology.sky130.sky130_scl") + name="Skywater 130nm Library", + grid_unit="0.001", + shrink_factor=None, + installs=[ + PathPrefix(id="$SKY130_NDA", + path="technology.sky130.sky130_nda"), + PathPrefix(id="$SKY130A", path="technology.sky130.sky130A"), + PathPrefix(id="$SKY130_CDS", + path="technology.sky130.sky130_cds"), + PathPrefix(id="$SKY130_SCL", + path="technology.sky130.sky130_scl") ], - libraries = libs, - gds_map_file = "sky130_lefpin.map", - physical_only_cells_list = phys_only, - dont_use_list = dont_use, - drc_decks = [ - DRCDeck(tool_name = "calibre", deck_name = "calibre_drc", path = "$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules"), - DRCDeck(tool_name = "klayout", deck_name = "klayout_drc", path = "$SKY130A/libs.tech/klayout/drc/sky130A.lydrc"), - DRCDeck(tool_name = "pegasus", deck_name = "pegasus_drc", path = "$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl") + libraries=libs, + gds_map_file="sky130_lefpin.map", + physical_only_cells_list=phys_only, + dont_use_list=dont_use, + drc_decks=[ + DRCDeck(tool_name="calibre", deck_name="calibre_drc", + path="$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules"), + DRCDeck(tool_name="klayout", deck_name="klayout_drc", + path="$SKY130A/libs.tech/klayout/drc/sky130A.lydrc"), + DRCDeck(tool_name="pegasus", deck_name="pegasus_drc", + path="$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl") ], - additional_drc_text = "", - lvs_decks = [ - LVSDeck(tool_name = "calibre", deck_name = "calibre_lvs", path = "$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8"), - LVSDeck(tool_name = "pegasus", deck_name = "pegasus_lvs", path = "$SKY130_CDS/Sky130_LVS/Sky130_rev_0.0_0.1.lvs.pvl") + additional_drc_text="", + lvs_decks=[ + LVSDeck(tool_name="calibre", deck_name="calibre_lvs", + path="$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8"), + LVSDeck(tool_name="pegasus", deck_name="pegasus_lvs", + path="$SKY130_CDS/Sky130_LVS/Sky130_rev_0.0_0.1.lvs.pvl") ], - additional_lvs_text = "", - tarballs = None, - sites = [ - Site(name = "unithd", x = Decimal("0.46"), y = Decimal("2.72")), - Site(name = "unithddbl", x = Decimal("0.46"), y = Decimal("5.44")) + additional_lvs_text="", + tarballs=None, + sites=[ + Site(name="unithd", x=Decimal("0.46"), y=Decimal("2.72")), + Site(name="unithddbl", x=Decimal("0.46"), y=Decimal("5.44")) ], - stackups = stackups, - special_cells = spcl_cells, - extra_prefixes = None + stackups=stackups, + special_cells=spcl_cells, + extra_prefixes=None ) 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_sram22 = os.path.exists(self.get_setting("technology.sky130.sram22_sky130_macros")) + self.use_sram22 = os.path.exists(self.get_setting( + "technology.sky130.sram22_sky130_macros")) if self.get_setting("technology.sky130.stdcell_library") == "sky130_fd_sc_hd": self.setup_cdl() self.setup_verilog() @@ -347,14 +430,14 @@ def post_install_script(self) -> None: self.setup_io_lefs() self.logger.info('Loaded Sky130 Tech') - def setup_cdl(self) -> None: ''' Copy and hack the cdl, replacing pfet_01v8_hvt/nfet_01v8 with respective names in LVS deck ''' setting_dir = self.get_setting("technology.sky130.sky130A") setting_dir = Path(setting_dir) - source_path = setting_dir / 'libs.ref' / self.library_name / 'cdl' / f'{self.library_name}.cdl' + 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}") @@ -375,11 +458,11 @@ def setup_cdl(self) -> None: 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)) + (source_path, dest_path)) df.write("*.SCALE MICRON\n") for line in sf: line = line.replace('pfet_01v8_hvt', pmos) - line = line.replace('nfet_01v8' , nmos) + line = line.replace('nfet_01v8', nmos) df.write(line) # Copy and hack the verilog @@ -393,7 +476,8 @@ def setup_verilog(self) -> None: slib = self.get_setting("technology.sky130.stdcell_library") # .v - source_path = Path(os.path.join(setting_dir, 'verilog', f'{slib}_9T.v')) + source_path = Path(os.path.join( + setting_dir, 'verilog', f'{slib}_9T.v')) if not source_path.exists(): raise FileNotFoundError(f"Verilog not found: {source_path}") @@ -404,10 +488,11 @@ def setup_verilog(self) -> None: 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)) + (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') + 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) if 0: @@ -417,32 +502,41 @@ def setup_verilog(self) -> None: sl = sf.readlines() # Find timing declaration - start_idx = [idx for idx, line in enumerate(sl) if "`ifndef SKY130_FD_SC_HD__LPFLOW_BLEEDER_1_TIMING_V" in line][0] + start_idx = [idx for idx, line in enumerate( + sl) if "`ifndef SKY130_FD_SC_HD__LPFLOW_BLEEDER_1_TIMING_V" in line][0] # Search for the broken statement search_range = range(start_idx+1, len(sl)) broken_specify_idx = len(sl)-1 broken_substr = "(SHORT => VPWR) = (0:0:0,0:0:0,0:0:0,0:0:0,0:0:0,0:0:0);" - broken_specify_idx = [idx for idx in search_range if broken_substr in sl[idx]][0] - endif_idx = [idx for idx in search_range if "`endif" in sl[idx]][0] + broken_specify_idx = [ + idx for idx in search_range if broken_substr in sl[idx]][0] + endif_idx = [ + idx for idx in search_range if "`endif" in sl[idx]][0] # Now, delete all the specify statements if specify exists before an endif. if broken_specify_idx < endif_idx: - self.logger.info("Removing incorrectly formed specify block.") + self.logger.info( + "Removing incorrectly formed specify block.") cell_def_range = range(start_idx+1, endif_idx) - start_specify_idx = [idx for idx in cell_def_range if "specify" in sl[idx]][0] - end_specify_idx = [idx for idx in cell_def_range if "endspecify" in sl[idx]][0] - sl[start_specify_idx:end_specify_idx+1] = [] # Dice + start_specify_idx = [ + idx for idx in cell_def_range if "specify" in sl[idx]][0] + end_specify_idx = [ + idx for idx in cell_def_range if "endspecify" in sl[idx]][0] + sl[start_specify_idx:end_specify_idx+1] = [] # Dice # Deal with the nonexistent net tactfully (don't code in brittle replacements) - self.logger.info("Fixing broken net references with select specify blocks.") + self.logger.info( + "Fixing broken net references with select specify blocks.") pattern = r"^\s*wire SLEEP.*B.*delayed;" capture_pattern = r".*(SLEEP.*?B.*?delayed).*" - pattern_idx = [(idx, re.findall(capture_pattern, value)[0]) for idx, value in enumerate(sl) if re.search(pattern, value)] + pattern_idx = [(idx, re.findall(capture_pattern, value)[0]) + for idx, value in enumerate(sl) if re.search(pattern, value)] for list_idx, pattern_tuple in enumerate(pattern_idx): if list_idx != len(pattern_idx)-1: - search_range = range(pattern_tuple[0]+1, pattern_idx[list_idx+1][0]) + search_range = range( + pattern_tuple[0]+1, pattern_idx[list_idx+1][0]) else: search_range = range(pattern_tuple[0]+1, len(sl)) for idx in search_range: @@ -450,14 +544,16 @@ def setup_verilog(self) -> None: for elem in list: if elem != pattern_tuple[1]: sl[idx] = sl[idx].replace(elem, pattern_tuple[1]) - self.logger.info(f"Incorrect reference `{elem}` to be replaced with: `{pattern_tuple[1]}` on raw line {idx}.") + self.logger.info( + f"Incorrect reference `{elem}` to be replaced with: `{pattern_tuple[1]}` on raw line {idx}.") # Write back into destination with open(dest_path, 'w') as df: df.writelines(sl) # primitives.v - source_path = setting_dir / 'libs.ref' / self.library_name / 'verilog' / 'primitives.v' + 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}") @@ -468,16 +564,25 @@ def setup_verilog(self) -> None: 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)) + (source_path, dest_path)) for line in sf: - line = line.replace('`default_nettype none','`default_nettype wire') + 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) - source_path = setting_dir / 'libs.ref' / self.library_name / 'techlef' / f'{self.library_name}__nom.tlef' + import pdb; pdb.set_trace() + slib = self.get_setting("technology.sky130.stdcell_library") + source_path = Path("") + if slib == "sky130_fd_sc_hd": + setting_dir = self.get_setting("technology.sky130.sky130A") + setting_dir = Path(setting_dir) + source_path = setting_dir / 'libs.ref' / self.library_name / \ + 'techlef' / f'{self.library_name}__nom.tlef' + elif slib == "sky130_scl": + source_path = Path(os.path.join(self.get_setting( + "technology.sky130.sky130_scl"), 'lef', f"{slib}_9T.tlef")) if not source_path.exists(): raise FileNotFoundError(f"Tech-LEF not found: {source_path}") @@ -488,7 +593,7 @@ def setup_techlef(self) -> None: 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)) + (source_path, dest_path)) for line in sf: df.write(line) if line.strip() == 'END pwell': @@ -499,7 +604,8 @@ def setup_techlef(self) -> None: # Current version has two errors in MACRO class definitions that break lef parser. def setup_io_lefs(self) -> None: sky130A_path = Path(self.get_setting('technology.sky130.sky130A')) - source_path = sky130A_path / 'libs.ref' / 'sky130_fd_io' / 'lef' / 'sky130_ef_io.lef' + source_path = sky130A_path / 'libs.ref' / \ + 'sky130_fd_io' / 'lef' / 'sky130_ef_io.lef' if not source_path.exists(): raise FileNotFoundError(f"IO LEF not found: {source_path}") @@ -510,42 +616,55 @@ def setup_io_lefs(self) -> None: with open(source_path, 'r') as sf: with open(dest_path, 'w') as df: self.logger.info("Modifying IO LEF: {} -> {}".format - (source_path, dest_path)) + (source_path, dest_path)) sl = sf.readlines() for net in ['VCCD1', 'VSSD1']: - start = [idx for idx,line in enumerate(sl) if 'PIN ' + net in line] - end = [idx for idx,line in enumerate(sl) if 'END ' + net in line] + start = [idx for idx, line in enumerate( + sl) if 'PIN ' + net in line] + end = [idx for idx, line in enumerate( + sl) if 'END ' + net in line] intervals = zip(start, end) for intv in intervals: - port_idx = [idx for idx,line in enumerate(sl[intv[0]:intv[1]]) if 'PORT' in line] + port_idx = [idx for idx, line in enumerate( + sl[intv[0]:intv[1]]) if 'PORT' in line] for idx in port_idx: - sl[intv[0]+idx]=sl[intv[0]+idx].replace('PORT', 'PORT\n CLASS CORE ;') + sl[intv[0]+idx] = sl[intv[0] + + idx].replace('PORT', 'PORT\n CLASS CORE ;') for cell in [ 'sky130_ef_io__connect_vcchib_vccd_and_vswitch_vddio_slice_20um', 'sky130_ef_io__disconnect_vccd_slice_5um', 'sky130_ef_io__disconnect_vdda_slice_5um', ]: # force class to spacer - start = [idx for idx, line in enumerate(sl) if f'MACRO {cell}' in line] - sl[start[0] + 1] = sl[start[0] + 1].replace('AREAIO', 'SPACER') + start = [idx for idx, line in enumerate( + sl) if f'MACRO {cell}' in line] + sl[start[0] + 1] = sl[start[0] + + 1].replace('AREAIO', 'SPACER') # Current version has two one-off error that break lef parser. - self.logger.info("Fixing broken sky130_ef_io__analog_esd_pad LEF definition.") - start_broken_macro_list = ["MACRO sky130_ef_io__analog_esd_pad\n", "MACRO sky130_ef_io__analog_pad\n"] - end_broken_macro_list = ["END sky130_ef_io__analog_pad\n", "END sky130_ef_io__analog_noesd_pad\n"] - end_fixed_macro_list = ["END sky130_ef_io__analog_esd_pad\n", "END sky130_ef_io__analog_pad\n"] + self.logger.info( + "Fixing broken sky130_ef_io__analog_esd_pad LEF definition.") + start_broken_macro_list = [ + "MACRO sky130_ef_io__analog_esd_pad\n", "MACRO sky130_ef_io__analog_pad\n"] + end_broken_macro_list = [ + "END sky130_ef_io__analog_pad\n", "END sky130_ef_io__analog_noesd_pad\n"] + end_fixed_macro_list = [ + "END sky130_ef_io__analog_esd_pad\n", "END sky130_ef_io__analog_pad\n"] for start_broken_macro, end_broken_macro, end_fixed_macro in zip(start_broken_macro_list, end_broken_macro_list, end_fixed_macro_list): # Get all start indices to be checked - start_check_indices = [idx for idx, line in enumerate(sl) if line == start_broken_macro] + start_check_indices = [idx for idx, line in enumerate( + sl) if line == start_broken_macro] # Extract broken macro - for idx_broken_macro in start_check_indices: + for idx_broken_macro in start_check_indices: # Find the start of the next_macro - idx_start_next_macro = [idx for idx in range(idx_broken_macro+1, len(sl)) if "MACRO" in sl[idx]][0] + idx_start_next_macro = [idx for idx in range( + idx_broken_macro+1, len(sl)) if "MACRO" in sl[idx]][0] # Find the broken macro ending idx_end_broken_macro = len(sl) - idx_end_broken_macro = [idx for idx in range(idx_broken_macro+1, len(sl)) if end_broken_macro in sl[idx]][0] + idx_end_broken_macro = [idx for idx in range( + idx_broken_macro+1, len(sl)) if end_broken_macro in sl[idx]][0] # Fix if idx_end_broken_macro < idx_start_next_macro: @@ -556,10 +675,14 @@ def setup_io_lefs(self) -> None: def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]: hooks = { "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_connect_nets), - HammerTool.make_pre_insertion_hook("write_design", sky130_connect_nets2) + 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_connect_nets), + HammerTool.make_pre_insertion_hook( + "write_design", sky130_connect_nets2) ]} return hooks.get(tool_name, []) @@ -567,23 +690,29 @@ def get_tech_drc_hooks(self, tool_name: str) -> List[HammerToolHookAction]: calibre_hooks = [] pegasus_hooks = [] if self.get_setting("technology.sky130.drc_blackbox_srams"): - calibre_hooks.append(HammerTool.make_post_insertion_hook("generate_drc_run_file", calibre_drc_blackbox_srams)) - pegasus_hooks.append(HammerTool.make_post_insertion_hook("generate_drc_ctl_file", pegasus_drc_blackbox_srams)) + calibre_hooks.append(HammerTool.make_post_insertion_hook( + "generate_drc_run_file", calibre_drc_blackbox_srams)) + pegasus_hooks.append(HammerTool.make_post_insertion_hook( + "generate_drc_ctl_file", pegasus_drc_blackbox_srams)) hooks = {"calibre": calibre_hooks, - "pegasus": pegasus_hooks + "pegasus": pegasus_hooks } return hooks.get(tool_name, []) def get_tech_lvs_hooks(self, tool_name: str) -> List[HammerToolHookAction]: - calibre_hooks = [HammerTool.make_post_insertion_hook("generate_lvs_run_file", setup_calibre_lvs_deck)] + calibre_hooks = [HammerTool.make_post_insertion_hook( + "generate_lvs_run_file", setup_calibre_lvs_deck)] pegasus_hooks = [] if self.use_sram22: - calibre_hooks.append(HammerTool.make_post_insertion_hook("generate_lvs_run_file", sram22_lvs_recognize_gates_all)) + calibre_hooks.append(HammerTool.make_post_insertion_hook( + "generate_lvs_run_file", sram22_lvs_recognize_gates_all)) if self.get_setting("technology.sky130.lvs_blackbox_srams"): - calibre_hooks.append(HammerTool.make_post_insertion_hook("generate_lvs_run_file", calibre_lvs_blackbox_srams)) - pegasus_hooks.append(HammerTool.make_post_insertion_hook("generate_lvs_ctl_file", pegasus_lvs_blackbox_srams)) + calibre_hooks.append(HammerTool.make_post_insertion_hook( + "generate_lvs_run_file", calibre_lvs_blackbox_srams)) + pegasus_hooks.append(HammerTool.make_post_insertion_hook( + "generate_lvs_ctl_file", pegasus_lvs_blackbox_srams)) hooks = {"calibre": calibre_hooks, - "pegasus": pegasus_hooks + "pegasus": pegasus_hooks } return hooks.get(tool_name, []) @@ -599,7 +728,8 @@ def openram_sram_names() -> List[str]: @staticmethod def sky130_sram_names() -> List[str]: sky130_sram_names = [] - sram_cache_json = importlib.resources.files("hammer.technology.sky130").joinpath("sram-cache.json").read_text() + sram_cache_json = importlib.resources.files( + "hammer.technology.sky130").joinpath("sram-cache.json").read_text() dl = json.loads(sram_cache_json) for d in dl: sky130_sram_names.append(d['name']) @@ -615,8 +745,10 @@ def sky130_sram_names() -> List[str]: # various Innovus database settings def sky130_innovus_settings(ht: HammerTool) -> bool: - assert isinstance(ht, HammerPlaceAndRouteTool), "Innovus settings only for par" - assert isinstance(ht, TCLTool), "innovus settings can only run on TCL tools" + assert isinstance( + ht, HammerPlaceAndRouteTool), "Innovus settings only for par" + assert isinstance( + ht, TCLTool), "innovus settings can only run on TCL tools" """Settings for every tool invocation""" ht.append( ''' @@ -680,28 +812,37 @@ def sky130_innovus_settings(ht: HammerTool) -> bool: ) 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" + 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 -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)) + if pwr_gnd_net.tie is not None: + 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 def sky130_add_endcaps(ht: HammerTool) -> bool: - assert isinstance(ht, HammerPlaceAndRouteTool), "endcap insertion only for par" - assert isinstance(ht, TCLTool), "endcap insertion can only run on TCL tools" - endcap_cells=ht.technology.get_special_cell_by_type(CellType.EndCap) - endcap_cell=endcap_cells[0].name[0] + assert isinstance( + ht, HammerPlaceAndRouteTool), "endcap insertion only for par" + assert isinstance( + ht, TCLTool), "endcap insertion can only run on TCL tools" + endcap_cells = ht.technology.get_special_cell_by_type(CellType.EndCap) + endcap_cell = endcap_cells[0].name[0] ht.append( f''' set_db add_endcaps_boundary_tap true @@ -712,9 +853,12 @@ def sky130_add_endcaps(ht: HammerTool) -> bool: ) return True + def efabless_ring_io(ht: HammerTool) -> bool: - assert isinstance(ht, HammerPlaceAndRouteTool), "IO ring instantiation only for par" - assert isinstance(ht, TCLTool), "IO ring instantiation can only run on TCL tools" + assert isinstance( + ht, HammerPlaceAndRouteTool), "IO ring instantiation only for par" + assert isinstance( + ht, TCLTool), "IO ring instantiation can only run on TCL tools" io_file = ht.get_setting("technology.sky130.io_file") ht.append(f"read_io_file {io_file} -no_die_size_adjust") p_nets = list(map(lambda s: s.name, ht.get_independent_power_nets())) @@ -755,6 +899,7 @@ def efabless_ring_io(ht: HammerTool) -> bool: ''') return True + def calibre_drc_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerDRCTool), "Exlude SRAMs only in DRC" drc_box = '' @@ -765,6 +910,7 @@ def calibre_drc_blackbox_srams(ht: HammerTool) -> bool: f.write(drc_box) return True + def pegasus_drc_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerDRCTool), "Exlude SRAMs only in DRC" drc_box = '' @@ -775,8 +921,10 @@ def pegasus_drc_blackbox_srams(ht: HammerTool) -> bool: f.write(drc_box) return True + def calibre_lvs_blackbox_srams(ht: HammerTool) -> bool: - assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" + assert isinstance( + ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" lvs_box = '' for name in SKY130Tech.sky130_sram_names(): lvs_box += f"\nLVS BOX {name}" @@ -786,15 +934,18 @@ def calibre_lvs_blackbox_srams(ht: HammerTool) -> bool: f.write(lvs_box) return True + def pegasus_lvs_blackbox_srams(ht: HammerTool) -> bool: - assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" + assert isinstance( + ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" lvs_box = '' for name in SKY130Tech.sky130_sram_names(): lvs_box += f"\nlvs_black_box {name} -gray" run_file = ht.lvs_ctl_file # type: ignore with open(run_file, "r+") as f: # Remove SRAM SPICE file includes. - pattern = 'schematic_path.*({}).*spice;\n'.format('|'.join(SKY130Tech.sky130_sram_names())) + pattern = 'schematic_path.*({}).*spice;\n'.format( + '|'.join(SKY130Tech.sky130_sram_names())) matcher = re.compile(pattern) contents = f.read() fixed_contents = matcher.sub("", contents) + lvs_box @@ -802,8 +953,10 @@ def pegasus_lvs_blackbox_srams(ht: HammerTool) -> bool: f.write(fixed_contents) return True + def sram22_lvs_recognize_gates_all(ht: HammerTool) -> bool: - assert isinstance(ht, HammerLVSTool), "Change 'LVS RECOGNIZE GATES' from 'NONE' to 'ALL' for SRAM22" + assert isinstance( + ht, HammerLVSTool), "Change 'LVS RECOGNIZE GATES' from 'NONE' to 'ALL' for SRAM22" run_file = ht.lvs_run_file # type: ignore with open(run_file, "a") as f: f.write("LVS RECOGNIZE GATES ALL") @@ -824,8 +977,10 @@ def sram22_lvs_recognize_gates_all(ht: HammerTool) -> bool: LVS FILTER D OPEN LAYOUT ''' + def setup_calibre_lvs_deck(ht: HammerTool) -> bool: - assert isinstance(ht, HammerLVSTool), "Modify Calibre LVS deck for LVS only" + assert isinstance( + ht, HammerLVSTool), "Modify Calibre LVS deck for LVS only" # Remove conflicting specification statements found in PDK LVS decks pattern = '.*({}).*\n'.format('|'.join(LVS_DECK_SCRUB_LINES)) matcher = re.compile(pattern) @@ -834,8 +989,9 @@ def setup_calibre_lvs_deck(ht: HammerTool) -> bool: lvs_decks = ht.technology.config.lvs_decks if not lvs_decks: return True - for i,deck in enumerate(lvs_decks): - if deck.tool_name != 'calibre': continue + for i, deck in enumerate(lvs_decks): + if deck.tool_name != 'calibre': + continue try: source_path = Path(source_paths[i]) except IndexError: @@ -849,7 +1005,7 @@ def setup_calibre_lvs_deck(ht: HammerTool) -> bool: with open(source_path, 'r') as sf: with open(dest_path, 'w') as df: ht.logger.info("Modifying LVS deck: {} -> {}".format - (source_path, dest_path)) + (source_path, dest_path)) df.write(matcher.sub("", sf.read())) df.write(LVS_DECK_INSERT_LINES) return True diff --git a/hammer/vlsi/units.py b/hammer/vlsi/units.py index 44230faec..679239dc6 100644 --- a/hammer/vlsi/units.py +++ b/hammer/vlsi/units.py @@ -82,7 +82,38 @@ class if one is not specified. match = re.search(regex, value) if match is None: try: - num = str(float(value)) + num = str(float(value.strip("\\"))) # TODO elam this is to fix the following ValueError + """ + Traceback (most recent call last): + File "/scratch/eecs251b-aae/chipyard/vlsi/./eecs251b-vlsi", line 36, in + Eecs251bDriver().main() + File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/cli_driver.py", line 1725, in main + sys.exit(self.run_main_parsed(vars(parser.parse_args(args)))) + File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/cli_driver.py", line 1630, in run_main_parsed + output_config = action_func(driver, errors.append) # type: Optional[dict] + File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/cli_driver.py", line 612, in action + success, output = driver.run_par( + File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/driver.py", line 1200, in run_par + run_succeeded = self.par_tool.run(hooks_to_use) + File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/hammer_tool.py", line 114, in run + if not self.run_steps(self.steps, hook_actions): + File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/hammer_tool.py", line 623, in run_steps + func_out = step.func(self) # type: bool + File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/hammer_tool.py", line 667, in wrapper + return func.__func__(x) # type: ignore # no type stub for builtin __func__ + File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/par/innovus/__init__.py", line 288, in init_design + self.write_contents_to_path(self.generate_mmmc_script(), mmmc_path) + File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/common/cadence/__init__.py", line 133, in generate_mmmc_script + sdc_files = self.generate_sdc_files() + File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/common/cadence/__init__.py", line 113, in generate_sdc_files + self.write_contents_to_path(self.sdc_pin_constraints, pin_constraints_fragment) + File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/hammer_vlsi_impl.py", line 2220, in sdc_pin_constraints + cap_unit = self.get_cap_unit().value_prefix + self.get_cap_unit().unit + File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/hammer_tool.py", line 1096, in get_cap_unit + return CapacitanceValue(get_or_else(self.technology.cap_unit, "1 pF")) + File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/units.py", line 88, in __init__ + raise ValueError("Malformed {type} value {value}".format(type=self.unit_type, +ValueError: Malformed capacitance value 1.000000\\ """ self._value_prefix = default_prefix except ValueError: raise ValueError("Malformed {type} value {value}".format(type=self.unit_type, From 00a9bf74201ead39f07f59b0b4bddcc8a8a95502 Mon Sep 17 00:00:00 2001 From: Elam Day-Friedland Date: Sat, 9 Mar 2024 18:49:37 -0800 Subject: [PATCH 15/68] readd 130a io libs --- hammer/technology/sky130/__init__.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index dce4b5234..17438713a 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -29,25 +29,20 @@ def gen_config(self) -> None: slib = self.get_setting("technology.sky130.stdcell_library") SKY130A = self.get_setting("technology.sky130.sky130A") SKY130_CDS = self.get_setting("technology.sky130.sky130_cds") - # TODO elam propogate to examples that this should look like "sky130_cds/LIB/sky130_scl_9T_0.0.2" on bwrc SKY130_CDS_LIB = self.get_setting("technology.sky130.sky130_scl") # Common tech LEF and IO cell spice netlists - # TODO elam use different techlef based on stdcell lib - libs = [] + libs = [Library(spice_file="$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io__analog.spice", + provides=[Provide(lib_type="IO library")])] if slib == "sky130_fd_sc_hd": - libs = [ - Library(lef_file="/sky130_fd_sc_hd__nom.tlef", + libs += [ + Library(lef_file="$SKY130A/sky130_fd_sc_hd__nom.tlef", verilog_sim="cache/primitives.v", provides=[Provide(lib_type="technology")]), - Library(spice_file="$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io__analog.spice", - provides=[Provide(lib_type="IO library")]) ] elif slib == "sky130_scl": - libs = [ + libs += [ Library(lef_file="$SKY130_SCL/lef/sky130_scl_9T.tlef", verilog_sim="$SKY130_SCL/verilog/sky130_scl_9T.v", provides=[Provide(lib_type="technology")]), - # Library(spice_file="$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io__analog.spice", - # provides=[Provide(lib_type="IO library")]) ] else: raise ValueError( @@ -427,7 +422,7 @@ def post_install_script(self) -> None: self.setup_cdl() self.setup_verilog() self.setup_techlef() - self.setup_io_lefs() + self.setup_io_lefs() self.logger.info('Loaded Sky130 Tech') def setup_cdl(self) -> None: From 8264cfa6ef664dea8646e4b76e536adf5dddf2f9 Mon Sep 17 00:00:00 2001 From: Elam Day-Friedland Date: Sat, 9 Mar 2024 20:41:43 -0800 Subject: [PATCH 16/68] set_setting for hammertech --- hammer/tech/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hammer/tech/__init__.py b/hammer/tech/__init__.py index cd40baa1a..7f4b9f709 100644 --- a/hammer/tech/__init__.py +++ b/hammer/tech/__init__.py @@ -419,6 +419,12 @@ def get_setting(self, key: str) -> Any: print(e) # TODO: fix the root cause return None + def set_setting(self, key: str, value: Any) -> None: + """ + Set a runtime setting in the database. + """ + self._database.set_setting(key, value) + def get_setting_suffix(self, key: str) -> Any: """Get a particular setting from the database with a suffix. """ From cd53650a8385246b3b327556cd83c14f342a4eb4 Mon Sep 17 00:00:00 2001 From: Elam Day-Friedland Date: Sat, 9 Mar 2024 21:27:09 -0800 Subject: [PATCH 17/68] perliminary (non-fully-functional) support for sky130_cdl. synthesis runs until genus breaks on trying to do clock gating with no latches- asking cadence folks on how to fix. --- hammer/synthesis/genus/__init__.py | 2 + hammer/technology/sky130/__init__.py | 85 ++++++++++------------------ hammer/vlsi/units.py | 33 +---------- 3 files changed, 33 insertions(+), 87 deletions(-) diff --git a/hammer/synthesis/genus/__init__.py b/hammer/synthesis/genus/__init__.py index a167f08b7..1a5af24bb 100644 --- a/hammer/synthesis/genus/__init__.py +++ b/hammer/synthesis/genus/__init__.py @@ -198,6 +198,8 @@ def init_environment(self) -> bool: verbose_append("set_db lp_clock_gating_prefix {CLKGATE}") verbose_append("set_db lp_insert_clock_gating true") verbose_append("set_db lp_clock_gating_register_aware true") + else: + verbose_append("set_db lp_clock_gating_infer_enable false") # Set up libraries. # Read timing libraries. diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 17438713a..69ff111dd 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -48,7 +48,6 @@ def gen_config(self) -> None: raise ValueError( f"Incorrect standard cell library selection: {slib}") # Generate IO cells - # TODO elam do this with cadence pdk? library = 'sky130_fd_io' SKYWATER_LIBS = os.path.join('$SKY130A', 'libs.ref', library) LIBRARY_PATH = os.path.join(SKY130A, 'libs.ref', library, 'lib') @@ -147,6 +146,7 @@ def gen_config(self) -> None: # Select standard cell libraries if slib == "sky130_fd_sc_hd": + phys_only = [ "sky130_fd_sc_hd__tap_1", "sky130_fd_sc_hd__tap_2", "sky130_fd_sc_hd__tapvgnd_1", "sky130_fd_sc_hd__tapvpwrvgnd_1", "sky130_fd_sc_hd__fill_1", "sky130_fd_sc_hd__fill_2", "sky130_fd_sc_hd__fill_4", "sky130_fd_sc_hd__fill_8", @@ -185,7 +185,7 @@ def gen_config(self) -> None: lib_corner_files = os.listdir(LIBRARY_PATH) lib_corner_files.sort() for cornerfilename in lib_corner_files: - if (not (library in cornerfilename)): + if (not ("sky130" in cornerfilename)): # cadence doesn't use the lib name in their corner libs continue if ('ccsnoise' in cornerfilename): continue # ignore duplicate corner.lib/corner_ccsnoise.lib files @@ -250,6 +250,9 @@ def gen_config(self) -> None: Stackup(name=slib, grid_unit=Decimal("0.001"), metals=metals)) elif slib == "sky130_scl": + # Cadence's stdcell library doesn't contain clock or power gate cells, so we can't use discrete clock gating + self.set_setting("synthesis.clock_gating_mode", "") + phys_only = [ "sky130_fd_sc_hd__tap_1", "sky130_fd_sc_hd__tap_2", "sky130_fd_sc_hd__tapvgnd_1", "sky130_fd_sc_hd__tapvpwrvgnd_1", "sky130_fd_sc_hd__fill_1", "sky130_fd_sc_hd__fill_2", "sky130_fd_sc_hd__fill_4", "sky130_fd_sc_hd__fill_8", @@ -260,35 +263,21 @@ def gen_config(self) -> None: "sky130_fd_sc_hd__probec_p_*" ] spcl_cells = [ - SpecialCell(cell_type="tiehilocell", name=[ - "sky130_fd_sc_hd__conb_1"]), - SpecialCell(cell_type="tiehicell", name=[ - "sky130_fd_sc_hd__conb_1"], output_ports=["HI"]), - SpecialCell(cell_type="tielocell", name=[ - "sky130_fd_sc_hd__conb_1"], output_ports=["LO"]), - SpecialCell(cell_type="endcap", name=[ - "sky130_fd_sc_hd__tap_1"]), - SpecialCell(cell_type="tapcell", name=[ - "sky130_fd_sc_hd__tapvpwrvgnd_1"]), - SpecialCell(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"]), - SpecialCell(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"]), + SpecialCell(cell_type="stdfiller", name= + [f"FILL{i**2}" for i in range(7)]), SpecialCell(cell_type="driver", name=[ - "sky130_fd_sc_hd__buf_4"], input_ports=["A"], output_ports=["X"]), - # TODO elam port the above ones too - # SpecialCell(cell_type = "ctsbuffer", name = ["sky130_fd_sc_hd__clkbuf_1"]) + "TBUF"], input_ports=["A"], output_ports=["Y"]), SpecialCell(cell_type="ctsbuffer", name=["CLKBUFX2"]) ] # Generate standard cell library library = slib - LIBRARY_PATH = SKY130_CDS_LIB + LIBRARY_PATH = os.path.join(SKY130_CDS_LIB, 'lib') lib_corner_files = os.listdir(LIBRARY_PATH) lib_corner_files.sort() for cornerfilename in lib_corner_files: - if (not (library in cornerfilename)): + if (not ("sky130" in cornerfilename)): # cadence doesn't use the lib name in their corner libs continue if ('ccsnoise' in cornerfilename): continue # ignore duplicate corner.lib/corner_ccsnoise.lib files @@ -297,35 +286,35 @@ def gen_config(self) -> None: if (tmp+'_ccsnoise.lib' in lib_corner_files): cornerfilename = tmp+'_ccsnoise.lib' # use ccsnoise version of lib file - cornername = tmp.split('__')[1] + cornername = tmp.replace("sky130_", "") cornerparts = cornername.split('_') + # Hardcode corners since they don't exactly match speed = cornerparts[0] + vdd = "" + temp = "" if (speed == 'ff'): + temp = "-40 C" + vdd = "1.95 V" speed = 'fast' if (speed == 'tt'): + vdd = "1.80 V" + temp = "25 C" speed = 'typical' if (speed == 'ss'): + vdd = "1.60 V" speed = 'slow' + temp = "100 C" - temp = cornerparts[1] - temp = temp.replace('n', '-') - temp = temp.split('C')[0]+' C' - - vdd = cornerparts[2] - vdd = vdd.split('v')[0]+'.'+vdd.split('v')[1]+' V' - - import pdb - pdb.set_trace() lib_entry = Library( nldm_liberty_file=os.path.join( - SKY130_CDS, 'lib', cornerfilename), + SKY130_CDS_LIB, 'lib', cornerfilename), verilog_sim=os.path.join( 'cache', library+'.v'), - lef_file=os.path.join(SKY130_CDS, 'lef', library+'.lef'), + lef_file=os.path.join(SKY130_CDS_LIB, 'lef', library+'_9T.lef'), spice_file=os.path.join( 'cache', library+'.cdl'), - gds_file=os.path.join(SKY130_CDS, 'gds', library+'.gds'), + gds_file=os.path.join(SKY130_CDS, 'gds', library+'_9T.gds'), corner=Corner( nmos=speed, pmos=speed, @@ -347,19 +336,6 @@ def gen_config(self) -> None: # Generate stackup metals = [] # type: List[Metal] - def is_float(string): - try: - float(string) - return True - except ValueError: - return False - - def get_min_from_line(line): - words = line.split() - nums = [float(w) for w in words if is_float(w)] - return min(nums) - - # TODO elam is 9t ok to hard code tlef_path = os.path.join(SKY130_CDS_LIB, 'lef', f"{slib}_9T.tlef") metals = list(map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path))) @@ -490,7 +466,6 @@ def setup_verilog(self) -> None: '`endif // SKY130_FD_SC_HD__LPFLOW_BLEEDER_FUNCTIONAL_V') df.write(line) - if 0: # Additionally hack out the specifies sl = [] with open(dest_path, 'r') as sf: @@ -567,7 +542,6 @@ def setup_verilog(self) -> None: # Copy and hack the tech-lef, adding this very important `licon` section def setup_techlef(self) -> None: - import pdb; pdb.set_trace() slib = self.get_setting("technology.sky130.stdcell_library") source_path = Path("") if slib == "sky130_fd_sc_hd": @@ -639,12 +613,12 @@ def setup_io_lefs(self) -> None: # Current version has two one-off error that break lef parser. self.logger.info( "Fixing broken sky130_ef_io__analog_esd_pad LEF definition.") - start_broken_macro_list = [ - "MACRO sky130_ef_io__analog_esd_pad\n", "MACRO sky130_ef_io__analog_pad\n"] - end_broken_macro_list = [ - "END sky130_ef_io__analog_pad\n", "END sky130_ef_io__analog_noesd_pad\n"] - end_fixed_macro_list = [ - "END sky130_ef_io__analog_esd_pad\n", "END sky130_ef_io__analog_pad\n"] + start_broken_macro_list = [] + #"MACRO sky130_ef_io__analog_esd_pad\n", "MACRO sky130_ef_io__analog_pad\n"] + end_broken_macro_list = [] + #"END sky130_ef_io__analog_pad\n", "END sky130_ef_io__analog_noesd_pad\n"] + end_fixed_macro_list = [] + #"END sky130_ef_io__analog_esd_pad\n", "END sky130_ef_io__analog_pad\n"] for start_broken_macro, end_broken_macro, end_fixed_macro in zip(start_broken_macro_list, end_broken_macro_list, end_fixed_macro_list): # Get all start indices to be checked @@ -805,6 +779,7 @@ def sky130_innovus_settings(ht: HammerTool) -> bool: set_db floorplan_snap_die_grid manufacturing ''' ) + return True diff --git a/hammer/vlsi/units.py b/hammer/vlsi/units.py index 679239dc6..f1a1beae3 100644 --- a/hammer/vlsi/units.py +++ b/hammer/vlsi/units.py @@ -82,38 +82,7 @@ class if one is not specified. match = re.search(regex, value) if match is None: try: - num = str(float(value.strip("\\"))) # TODO elam this is to fix the following ValueError - """ - Traceback (most recent call last): - File "/scratch/eecs251b-aae/chipyard/vlsi/./eecs251b-vlsi", line 36, in - Eecs251bDriver().main() - File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/cli_driver.py", line 1725, in main - sys.exit(self.run_main_parsed(vars(parser.parse_args(args)))) - File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/cli_driver.py", line 1630, in run_main_parsed - output_config = action_func(driver, errors.append) # type: Optional[dict] - File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/cli_driver.py", line 612, in action - success, output = driver.run_par( - File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/driver.py", line 1200, in run_par - run_succeeded = self.par_tool.run(hooks_to_use) - File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/hammer_tool.py", line 114, in run - if not self.run_steps(self.steps, hook_actions): - File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/hammer_tool.py", line 623, in run_steps - func_out = step.func(self) # type: bool - File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/hammer_tool.py", line 667, in wrapper - return func.__func__(x) # type: ignore # no type stub for builtin __func__ - File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/par/innovus/__init__.py", line 288, in init_design - self.write_contents_to_path(self.generate_mmmc_script(), mmmc_path) - File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/common/cadence/__init__.py", line 133, in generate_mmmc_script - sdc_files = self.generate_sdc_files() - File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/common/cadence/__init__.py", line 113, in generate_sdc_files - self.write_contents_to_path(self.sdc_pin_constraints, pin_constraints_fragment) - File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/hammer_vlsi_impl.py", line 2220, in sdc_pin_constraints - cap_unit = self.get_cap_unit().value_prefix + self.get_cap_unit().unit - File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/hammer_tool.py", line 1096, in get_cap_unit - return CapacitanceValue(get_or_else(self.technology.cap_unit, "1 pF")) - File "/scratch/eecs251b-aae/chipyard/vlsi/hammer/hammer/vlsi/units.py", line 88, in __init__ - raise ValueError("Malformed {type} value {value}".format(type=self.unit_type, -ValueError: Malformed capacitance value 1.000000\\ """ + num = str(float(value.strip("\\"))) self._value_prefix = default_prefix except ValueError: raise ValueError("Malformed {type} value {value}".format(type=self.unit_type, From b6094f3e8ee49d31a195e25878c91fc8f0f54379 Mon Sep 17 00:00:00 2001 From: Elam Day-Friedland Date: Sat, 9 Mar 2024 21:53:07 -0800 Subject: [PATCH 18/68] take out unused hacks --- hammer/technology/sky130/__init__.py | 120 +++++++++++---------------- 1 file changed, 50 insertions(+), 70 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 69ff111dd..81d5908d3 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -442,13 +442,11 @@ def setup_cdl(self) -> None: # (the open-source RTL sim tools don't treat undeclared signals as errors) # - Deal with numerous inconsistencies in timing specify blocks. def setup_verilog(self) -> None: - setting_dir = self.get_setting("technology.sky130.sky130_scl") + setting_dir = self.get_setting("technology.sky130.sky130A") setting_dir = Path(setting_dir) - slib = self.get_setting("technology.sky130.stdcell_library") # .v - source_path = Path(os.path.join( - setting_dir, 'verilog', f'{slib}_9T.v')) + 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}") @@ -466,60 +464,50 @@ def setup_verilog(self) -> None: '`endif // SKY130_FD_SC_HD__LPFLOW_BLEEDER_FUNCTIONAL_V') df.write(line) - # Additionally hack out the specifies - sl = [] - with open(dest_path, 'r') as sf: - sl = sf.readlines() - - # Find timing declaration - start_idx = [idx for idx, line in enumerate( - sl) if "`ifndef SKY130_FD_SC_HD__LPFLOW_BLEEDER_1_TIMING_V" in line][0] - - # Search for the broken statement - search_range = range(start_idx+1, len(sl)) - broken_specify_idx = len(sl)-1 - broken_substr = "(SHORT => VPWR) = (0:0:0,0:0:0,0:0:0,0:0:0,0:0:0,0:0:0);" - - broken_specify_idx = [ - idx for idx in search_range if broken_substr in sl[idx]][0] - endif_idx = [ - idx for idx in search_range if "`endif" in sl[idx]][0] - - # Now, delete all the specify statements if specify exists before an endif. - if broken_specify_idx < endif_idx: - self.logger.info( - "Removing incorrectly formed specify block.") - cell_def_range = range(start_idx+1, endif_idx) - start_specify_idx = [ - idx for idx in cell_def_range if "specify" in sl[idx]][0] - end_specify_idx = [ - idx for idx in cell_def_range if "endspecify" in sl[idx]][0] - sl[start_specify_idx:end_specify_idx+1] = [] # Dice - - # Deal with the nonexistent net tactfully (don't code in brittle replacements) - self.logger.info( - "Fixing broken net references with select specify blocks.") - pattern = r"^\s*wire SLEEP.*B.*delayed;" - capture_pattern = r".*(SLEEP.*?B.*?delayed).*" - pattern_idx = [(idx, re.findall(capture_pattern, value)[0]) - for idx, value in enumerate(sl) if re.search(pattern, value)] - for list_idx, pattern_tuple in enumerate(pattern_idx): - if list_idx != len(pattern_idx)-1: - search_range = range( - pattern_tuple[0]+1, pattern_idx[list_idx+1][0]) - else: - search_range = range(pattern_tuple[0]+1, len(sl)) - for idx in search_range: - list = re.findall(capture_pattern, sl[idx]) - for elem in list: - if elem != pattern_tuple[1]: - sl[idx] = sl[idx].replace(elem, pattern_tuple[1]) - self.logger.info( - f"Incorrect reference `{elem}` to be replaced with: `{pattern_tuple[1]}` on raw line {idx}.") - - # Write back into destination - with open(dest_path, 'w') as df: - df.writelines(sl) + # Additionally hack out the specifies + sl = [] + with open(dest_path, 'r') as sf: + sl = sf.readlines() + + # Find timing declaration + start_idx = [idx for idx, line in enumerate(sl) if "`ifndef SKY130_FD_SC_HD__LPFLOW_BLEEDER_1_TIMING_V" in line][0] + + # Search for the broken statement + search_range = range(start_idx+1, len(sl)) + broken_specify_idx = len(sl)-1 + broken_substr = "(SHORT => VPWR) = (0:0:0,0:0:0,0:0:0,0:0:0,0:0:0,0:0:0);" + + broken_specify_idx = [idx for idx in search_range if broken_substr in sl[idx]][0] + endif_idx = [idx for idx in search_range if "`endif" in sl[idx]][0] + + # Now, delete all the specify statements if specify exists before an endif. + if broken_specify_idx < endif_idx: + self.logger.info("Removing incorrectly formed specify block.") + cell_def_range = range(start_idx+1, endif_idx) + start_specify_idx = [idx for idx in cell_def_range if "specify" in sl[idx]][0] + end_specify_idx = [idx for idx in cell_def_range if "endspecify" in sl[idx]][0] + sl[start_specify_idx:end_specify_idx+1] = [] # Dice + + # Deal with the nonexistent net tactfully (don't code in brittle replacements) + self.logger.info("Fixing broken net references with select specify blocks.") + pattern = r"^\s*wire SLEEP.*B.*delayed;" + capture_pattern = r".*(SLEEP.*?B.*?delayed).*" + pattern_idx = [(idx, re.findall(capture_pattern, value)[0]) for idx, value in enumerate(sl) if re.search(pattern, value)] + for list_idx, pattern_tuple in enumerate(pattern_idx): + if list_idx != len(pattern_idx)-1: + search_range = range(pattern_tuple[0]+1, pattern_idx[list_idx+1][0]) + else: + search_range = range(pattern_tuple[0]+1, len(sl)) + for idx in search_range: + list = re.findall(capture_pattern, sl[idx]) + for elem in list: + if elem != pattern_tuple[1]: + sl[idx] = sl[idx].replace(elem, pattern_tuple[1]) + self.logger.info(f"Incorrect reference `{elem}` to be replaced with: `{pattern_tuple[1]}` on raw line {idx}.") + + # Write back into destination + with open(dest_path, 'w') as df: + df.writelines(sl) # primitives.v source_path = setting_dir / 'libs.ref' / \ @@ -536,22 +524,14 @@ def setup_verilog(self) -> None: self.logger.info("Modifying Verilog netlist: {} -> {}".format (source_path, dest_path)) for line in sf: - line = line.replace( - '`default_nettype none', '`default_nettype wire') + 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: - slib = self.get_setting("technology.sky130.stdcell_library") - source_path = Path("") - if slib == "sky130_fd_sc_hd": - setting_dir = self.get_setting("technology.sky130.sky130A") - setting_dir = Path(setting_dir) - source_path = setting_dir / 'libs.ref' / self.library_name / \ - 'techlef' / f'{self.library_name}__nom.tlef' - elif slib == "sky130_scl": - source_path = Path(os.path.join(self.get_setting( - "technology.sky130.sky130_scl"), 'lef', f"{slib}_9T.tlef")) + setting_dir = self.get_setting("technology.sky130.sky130A") + setting_dir = Path(setting_dir) + 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}") From 88ef8f3a1816f2d6705e4fd6fc0ea5bd27e446d4 Mon Sep 17 00:00:00 2001 From: Elam Day-Friedland Date: Sat, 9 Mar 2024 22:11:05 -0800 Subject: [PATCH 19/68] take out pad io lef hack --- hammer/technology/sky130/__init__.py | 80 ++-------------------------- 1 file changed, 4 insertions(+), 76 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 81d5908d3..d6bbe4096 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -96,13 +96,15 @@ def gen_config(self) -> None: if cell_name == 'sky130_ef_io__gpiov2_pad_wrapped': file_lib = 'sky130_ef_io' gds_file = cell_name + '.gds' - lef_file = 'cache/sky130_ef_io.lef' + lef_file = os.path.join( + SKY130A, 'libs.ref', library, 'lef', "sky130_ef_io.lef") spice_file = os.path.join( SKYWATER_LIBS, 'cdl', file_lib + '.cdl') elif 'sky130_ef_io' in cell_name: file_lib = 'sky130_ef_io' gds_file = file_lib + '.gds' - lef_file = 'cache/' + file_lib + '.lef' + lef_file = os.path.join( + SKY130A, 'libs.ref', library, 'lef', "sky130_ef_io.lef") spice_file = os.path.join( SKYWATER_LIBS, 'cdl', file_lib + '.cdl') else: @@ -398,7 +400,6 @@ def post_install_script(self) -> None: self.setup_cdl() self.setup_verilog() self.setup_techlef() - self.setup_io_lefs() self.logger.info('Loaded Sky130 Tech') def setup_cdl(self) -> None: @@ -548,79 +549,6 @@ def setup_techlef(self) -> None: if line.strip() == 'END pwell': df.write(_the_tlef_edit) - # Power pins for clamps must be CLASS CORE - # connect/disconnect spacers must be CLASS PAD SPACER, not AREAIO - # Current version has two errors in MACRO class definitions that break lef parser. - def setup_io_lefs(self) -> None: - sky130A_path = Path(self.get_setting('technology.sky130.sky130A')) - source_path = sky130A_path / 'libs.ref' / \ - 'sky130_fd_io' / 'lef' / 'sky130_ef_io.lef' - if not source_path.exists(): - raise FileNotFoundError(f"IO LEF not found: {source_path}") - - cache_tech_dir_path = Path(self.cache_dir) - os.makedirs(cache_tech_dir_path, exist_ok=True) - dest_path = cache_tech_dir_path / 'sky130_ef_io.lef' - - with open(source_path, 'r') as sf: - with open(dest_path, 'w') as df: - self.logger.info("Modifying IO LEF: {} -> {}".format - (source_path, dest_path)) - sl = sf.readlines() - for net in ['VCCD1', 'VSSD1']: - start = [idx for idx, line in enumerate( - sl) if 'PIN ' + net in line] - end = [idx for idx, line in enumerate( - sl) if 'END ' + net in line] - intervals = zip(start, end) - for intv in intervals: - port_idx = [idx for idx, line in enumerate( - sl[intv[0]:intv[1]]) if 'PORT' in line] - for idx in port_idx: - sl[intv[0]+idx] = sl[intv[0] + - idx].replace('PORT', 'PORT\n CLASS CORE ;') - for cell in [ - 'sky130_ef_io__connect_vcchib_vccd_and_vswitch_vddio_slice_20um', - 'sky130_ef_io__disconnect_vccd_slice_5um', - 'sky130_ef_io__disconnect_vdda_slice_5um', - ]: - # force class to spacer - start = [idx for idx, line in enumerate( - sl) if f'MACRO {cell}' in line] - sl[start[0] + 1] = sl[start[0] + - 1].replace('AREAIO', 'SPACER') - - # Current version has two one-off error that break lef parser. - self.logger.info( - "Fixing broken sky130_ef_io__analog_esd_pad LEF definition.") - start_broken_macro_list = [] - #"MACRO sky130_ef_io__analog_esd_pad\n", "MACRO sky130_ef_io__analog_pad\n"] - end_broken_macro_list = [] - #"END sky130_ef_io__analog_pad\n", "END sky130_ef_io__analog_noesd_pad\n"] - end_fixed_macro_list = [] - #"END sky130_ef_io__analog_esd_pad\n", "END sky130_ef_io__analog_pad\n"] - - for start_broken_macro, end_broken_macro, end_fixed_macro in zip(start_broken_macro_list, end_broken_macro_list, end_fixed_macro_list): - # Get all start indices to be checked - start_check_indices = [idx for idx, line in enumerate( - sl) if line == start_broken_macro] - - # Extract broken macro - for idx_broken_macro in start_check_indices: - # Find the start of the next_macro - idx_start_next_macro = [idx for idx in range( - idx_broken_macro+1, len(sl)) if "MACRO" in sl[idx]][0] - # Find the broken macro ending - idx_end_broken_macro = len(sl) - idx_end_broken_macro = [idx for idx in range( - idx_broken_macro+1, len(sl)) if end_broken_macro in sl[idx]][0] - - # Fix - if idx_end_broken_macro < idx_start_next_macro: - sl[idx_end_broken_macro] = end_fixed_macro - - df.writelines(sl) - def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]: hooks = { "innovus": [ From ff937d6f355eedc702482e6cfbfae5278ed55fc1 Mon Sep 17 00:00:00 2001 From: Elam Day-Friedland Date: Sun, 10 Mar 2024 19:14:50 -0700 Subject: [PATCH 20/68] undo autoformatting --- hammer/technology/sky130/__init__.py | 112 +++++++++------------------ 1 file changed, 35 insertions(+), 77 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index d6bbe4096..a13f21ddd 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -255,15 +255,6 @@ def gen_config(self) -> None: # Cadence's stdcell library doesn't contain clock or power gate cells, so we can't use discrete clock gating self.set_setting("synthesis.clock_gating_mode", "") - phys_only = [ - "sky130_fd_sc_hd__tap_1", "sky130_fd_sc_hd__tap_2", "sky130_fd_sc_hd__tapvgnd_1", "sky130_fd_sc_hd__tapvpwrvgnd_1", - "sky130_fd_sc_hd__fill_1", "sky130_fd_sc_hd__fill_2", "sky130_fd_sc_hd__fill_4", "sky130_fd_sc_hd__fill_8", - "sky130_fd_sc_hd__diode_2"] - dont_use = [ - "*sdf*", - "sky130_fd_sc_hd__probe_p_*", - "sky130_fd_sc_hd__probec_p_*" - ] spcl_cells = [ SpecialCell(cell_type="stdfiller", name= [f"FILL{i**2}" for i in range(7)]), @@ -291,7 +282,7 @@ def gen_config(self) -> None: cornername = tmp.replace("sky130_", "") cornerparts = cornername.split('_') - # Hardcode corners since they don't exactly match + # Hardcode corner annotations since they don't exactly match the sky130a speed = cornerparts[0] vdd = "" temp = "" @@ -394,22 +385,21 @@ def gen_config(self) -> None: 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_sram22 = os.path.exists(self.get_setting( - "technology.sky130.sram22_sky130_macros")) + self.use_sram22 = os.path.exists(self.get_setting("technology.sky130.sram22_sky130_macros")) if self.get_setting("technology.sky130.stdcell_library") == "sky130_fd_sc_hd": self.setup_cdl() self.setup_verilog() self.setup_techlef() self.logger.info('Loaded Sky130 Tech') + def setup_cdl(self) -> None: ''' Copy and hack the cdl, replacing pfet_01v8_hvt/nfet_01v8 with respective names in LVS deck ''' setting_dir = self.get_setting("technology.sky130.sky130A") setting_dir = Path(setting_dir) - source_path = setting_dir / 'libs.ref' / \ - self.library_name / 'cdl' / f'{self.library_name}.cdl' + 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}") @@ -430,7 +420,7 @@ def setup_cdl(self) -> None: 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)) + (source_path, dest_path)) df.write("*.SCALE MICRON\n") for line in sf: line = line.replace('pfet_01v8_hvt', pmos) @@ -552,14 +542,10 @@ def setup_techlef(self) -> None: def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]: hooks = { "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_connect_nets), - HammerTool.make_pre_insertion_hook( - "write_design", sky130_connect_nets2) + 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_connect_nets), + HammerTool.make_pre_insertion_hook( "write_design", sky130_connect_nets2) ]} return hooks.get(tool_name, []) @@ -567,27 +553,21 @@ def get_tech_drc_hooks(self, tool_name: str) -> List[HammerToolHookAction]: calibre_hooks = [] pegasus_hooks = [] if self.get_setting("technology.sky130.drc_blackbox_srams"): - calibre_hooks.append(HammerTool.make_post_insertion_hook( - "generate_drc_run_file", calibre_drc_blackbox_srams)) - pegasus_hooks.append(HammerTool.make_post_insertion_hook( - "generate_drc_ctl_file", pegasus_drc_blackbox_srams)) + calibre_hooks.append(HammerTool.make_post_insertion_hook("generate_drc_run_file", calibre_drc_blackbox_srams)) + pegasus_hooks.append(HammerTool.make_post_insertion_hook("generate_drc_ctl_file", pegasus_drc_blackbox_srams)) hooks = {"calibre": calibre_hooks, - "pegasus": pegasus_hooks + "pegasus": pegasus_hooks } return hooks.get(tool_name, []) def get_tech_lvs_hooks(self, tool_name: str) -> List[HammerToolHookAction]: - calibre_hooks = [HammerTool.make_post_insertion_hook( - "generate_lvs_run_file", setup_calibre_lvs_deck)] + calibre_hooks = [HammerTool.make_post_insertion_hook("generate_lvs_run_file", setup_calibre_lvs_deck)] pegasus_hooks = [] if self.use_sram22: - calibre_hooks.append(HammerTool.make_post_insertion_hook( - "generate_lvs_run_file", sram22_lvs_recognize_gates_all)) + calibre_hooks.append(HammerTool.make_post_insertion_hook("generate_lvs_run_file", sram22_lvs_recognize_gates_all)) if self.get_setting("technology.sky130.lvs_blackbox_srams"): - calibre_hooks.append(HammerTool.make_post_insertion_hook( - "generate_lvs_run_file", calibre_lvs_blackbox_srams)) - pegasus_hooks.append(HammerTool.make_post_insertion_hook( - "generate_lvs_ctl_file", pegasus_lvs_blackbox_srams)) + calibre_hooks.append(HammerTool.make_post_insertion_hook("generate_lvs_run_file", calibre_lvs_blackbox_srams)) + pegasus_hooks.append(HammerTool.make_post_insertion_hook("generate_lvs_ctl_file", pegasus_lvs_blackbox_srams)) hooks = {"calibre": calibre_hooks, "pegasus": pegasus_hooks } @@ -605,8 +585,7 @@ def openram_sram_names() -> List[str]: @staticmethod def sky130_sram_names() -> List[str]: sky130_sram_names = [] - sram_cache_json = importlib.resources.files( - "hammer.technology.sky130").joinpath("sram-cache.json").read_text() + sram_cache_json = importlib.resources.files("hammer.technology.sky130").joinpath("sram-cache.json").read_text() dl = json.loads(sram_cache_json) for d in dl: sky130_sram_names.append(d['name']) @@ -622,10 +601,8 @@ def sky130_sram_names() -> List[str]: # various Innovus database settings def sky130_innovus_settings(ht: HammerTool) -> bool: - assert isinstance( - ht, HammerPlaceAndRouteTool), "Innovus settings only for par" - assert isinstance( - ht, TCLTool), "innovus settings can only run on TCL tools" + assert isinstance(ht, HammerPlaceAndRouteTool), "Innovus settings only for par" + assert isinstance(ht, TCLTool), "innovus settings can only run on TCL tools" """Settings for every tool invocation""" ht.append( ''' @@ -697,30 +674,24 @@ def sky130_connect_nets(ht: HammerTool) -> bool: 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 -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)) + if pwr_gnd_net.tie is not None: + 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 def sky130_add_endcaps(ht: HammerTool) -> bool: - assert isinstance( - ht, HammerPlaceAndRouteTool), "endcap insertion only for par" - assert isinstance( - ht, TCLTool), "endcap insertion can only run on TCL tools" - endcap_cells = ht.technology.get_special_cell_by_type(CellType.EndCap) - endcap_cell = endcap_cells[0].name[0] + assert isinstance(ht, HammerPlaceAndRouteTool), "endcap insertion only for par" + assert isinstance(ht, TCLTool), "endcap insertion can only run on TCL tools" + endcap_cells=ht.technology.get_special_cell_by_type(CellType.EndCap) + endcap_cell=endcap_cells[0].name[0] ht.append( f''' set_db add_endcaps_boundary_tap true @@ -731,12 +702,9 @@ def sky130_add_endcaps(ht: HammerTool) -> bool: ) return True - def efabless_ring_io(ht: HammerTool) -> bool: - assert isinstance( - ht, HammerPlaceAndRouteTool), "IO ring instantiation only for par" - assert isinstance( - ht, TCLTool), "IO ring instantiation can only run on TCL tools" + assert isinstance(ht, HammerPlaceAndRouteTool), "IO ring instantiation only for par" + assert isinstance(ht, TCLTool), "IO ring instantiation can only run on TCL tools" io_file = ht.get_setting("technology.sky130.io_file") ht.append(f"read_io_file {io_file} -no_die_size_adjust") p_nets = list(map(lambda s: s.name, ht.get_independent_power_nets())) @@ -777,7 +745,6 @@ def efabless_ring_io(ht: HammerTool) -> bool: ''') return True - def calibre_drc_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerDRCTool), "Exlude SRAMs only in DRC" drc_box = '' @@ -788,7 +755,6 @@ def calibre_drc_blackbox_srams(ht: HammerTool) -> bool: f.write(drc_box) return True - def pegasus_drc_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerDRCTool), "Exlude SRAMs only in DRC" drc_box = '' @@ -799,10 +765,8 @@ def pegasus_drc_blackbox_srams(ht: HammerTool) -> bool: f.write(drc_box) return True - def calibre_lvs_blackbox_srams(ht: HammerTool) -> bool: - assert isinstance( - ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" + assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" lvs_box = '' for name in SKY130Tech.sky130_sram_names(): lvs_box += f"\nLVS BOX {name}" @@ -812,18 +776,15 @@ def calibre_lvs_blackbox_srams(ht: HammerTool) -> bool: f.write(lvs_box) return True - def pegasus_lvs_blackbox_srams(ht: HammerTool) -> bool: - assert isinstance( - ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" + assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" lvs_box = '' for name in SKY130Tech.sky130_sram_names(): lvs_box += f"\nlvs_black_box {name} -gray" run_file = ht.lvs_ctl_file # type: ignore with open(run_file, "r+") as f: # Remove SRAM SPICE file includes. - pattern = 'schematic_path.*({}).*spice;\n'.format( - '|'.join(SKY130Tech.sky130_sram_names())) + pattern = 'schematic_path.*({}).*spice;\n'.format('|'.join(SKY130Tech.sky130_sram_names())) matcher = re.compile(pattern) contents = f.read() fixed_contents = matcher.sub("", contents) + lvs_box @@ -831,10 +792,8 @@ def pegasus_lvs_blackbox_srams(ht: HammerTool) -> bool: f.write(fixed_contents) return True - def sram22_lvs_recognize_gates_all(ht: HammerTool) -> bool: - assert isinstance( - ht, HammerLVSTool), "Change 'LVS RECOGNIZE GATES' from 'NONE' to 'ALL' for SRAM22" + assert isinstance(ht, HammerLVSTool), "Change 'LVS RECOGNIZE GATES' from 'NONE' to 'ALL' for SRAM22" run_file = ht.lvs_run_file # type: ignore with open(run_file, "a") as f: f.write("LVS RECOGNIZE GATES ALL") @@ -867,9 +826,8 @@ def setup_calibre_lvs_deck(ht: HammerTool) -> bool: lvs_decks = ht.technology.config.lvs_decks if not lvs_decks: return True - for i, deck in enumerate(lvs_decks): - if deck.tool_name != 'calibre': - continue + for i,deck in enumerate(lvs_decks): + if deck.tool_name != 'calibre': continue try: source_path = Path(source_paths[i]) except IndexError: @@ -883,7 +841,7 @@ def setup_calibre_lvs_deck(ht: HammerTool) -> bool: with open(source_path, 'r') as sf: with open(dest_path, 'w') as df: ht.logger.info("Modifying LVS deck: {} -> {}".format - (source_path, dest_path)) + (source_path, dest_path)) df.write(matcher.sub("", sf.read())) df.write(LVS_DECK_INSERT_LINES) return True From 1ba69b2240bfd33b401829358e23f4f36d50a5ab Mon Sep 17 00:00:00 2001 From: elamdf Date: Wed, 3 Apr 2024 19:44:02 -0700 Subject: [PATCH 21/68] synthesis runs for commercial tutorial flow. par fails (either in place_tap_cells or in power_straps if you force it past place_tap_cells with a removal hook due to no tapcells) --- e2e/configs-env/bwrc-env.yml | 3 ++- e2e/pdks/sky130-bwrc.yml | 4 +++- hammer/technology/sky130/__init__.py | 22 ++++++++++++++++++---- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/e2e/configs-env/bwrc-env.yml b/e2e/configs-env/bwrc-env.yml index 613304ede..e4fc2429e 100644 --- a/e2e/configs-env/bwrc-env.yml +++ b/e2e/configs-env/bwrc-env.yml @@ -33,7 +33,8 @@ technology.sky130: sram22_sky130_macros: "/tools/commercial/skywater/local/sram22_sky130_macros" openram_lib: "/tools/commercial/skywater/local/sky130_sram_macros" sky130_nda: "/tools/commercial/skywater/swtech130/skywater-src-nda-20221031" - sky130_cds: "/tools/commercial/skywater/sky130_cds/sky130_prelim_release_091123" + sky130_cds: "/tools/commercial/skywater/sky130_cds/PDK/sky130_release_0.0.3/" + sky130_scl: "/tools/commercial/skywater/sky130_cds/LIB/sky130_scl_9T_0.0.3/" # ASAP7 paths technology.asap7: diff --git a/e2e/pdks/sky130-bwrc.yml b/e2e/pdks/sky130-bwrc.yml index e7feb9a04..0d75b7862 100644 --- a/e2e/pdks/sky130-bwrc.yml +++ b/e2e/pdks/sky130-bwrc.yml @@ -5,8 +5,10 @@ technology.sky130: openram_lib: "/tools/commercial/skywater/local/sky130_sram_macros" sky130_nda: "/tools/commercial/skywater/swtech130/skywater-src-nda-20221031" sky130_cds: "/tools/commercial/skywater/sky130_cds/sky130_prelim_release_091123" + sky130_cds: "/tools/commercial/skywater/sky130_cds/PDK/sky130_release_0.0.3/" + sky130_scl: "/tools/commercial/skywater/sky130_cds/LIB/sky130_scl_9T_0.0.3/" -synthesis.genus.version: "211" +synthesis.genus.version: "221" par.innovus.version: "211" sim.vcs.version: "S-2021.09-SP1-1" diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index a13f21ddd..9fdf223ed 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -252,15 +252,17 @@ def gen_config(self) -> None: Stackup(name=slib, grid_unit=Decimal("0.001"), metals=metals)) elif slib == "sky130_scl": - # Cadence's stdcell library doesn't contain clock or power gate cells, so we can't use discrete clock gating - self.set_setting("synthesis.clock_gating_mode", "") + # The cadence PDK (as of version 0.0.3) doesn't seem to have tap nor decap cells, so par won't run (and if we forced it to, lvs would fail) spcl_cells = [ SpecialCell(cell_type="stdfiller", name= [f"FILL{i**2}" for i in range(7)]), SpecialCell(cell_type="driver", name=[ "TBUF"], input_ports=["A"], output_ports=["Y"]), - SpecialCell(cell_type="ctsbuffer", name=["CLKBUFX2"]) + SpecialCell(cell_type="ctsbuffer", name=["CLKBUFX2"]), + # NOTE: this cell is marked don't use/touch in v0.0.3 of the cadence stdcell library. we're bypassing this with `enable_scl_clk_gating_cell`. + SpecialCell(cell_type=CellType("ctsgate"), + name=["ICGX1"]) ] # Generate standard cell library @@ -399,7 +401,7 @@ def setup_cdl(self) -> None: ''' setting_dir = self.get_setting("technology.sky130.sky130A") setting_dir = Path(setting_dir) - source_path = setting_dir / 'libs.ref' / \ self.library_name / 'cdl' / f'{self.library_name}.cdl' + 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}") @@ -538,6 +540,18 @@ def setup_techlef(self) -> None: df.write(line) if line.strip() == 'END pwell': df.write(_the_tlef_edit) + def get_tech_syn_hooks(self, tool_name: str) -> List[HammerToolHookAction]: + hooks = {} + + def enable_scl_clk_gating_cell(ht: HammerTool) -> bool: + ht.append("set_db [get_db lib_cells -if {.base_name == ICGX1}] .avoid false") + return True + + # The clock gating cell is set to don't touch/use in the cadence pdk (as of v0.0.3), work around that + if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": + hooks["genus"] = [HammerTool.make_pre_insertion_hook("syn_generic", enable_scl_clk_gating_cell)] + + return hooks.get(tool_name, []) def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]: hooks = { From adcdfaee62b22d6a270e0bb3055642adb26e9bc7 Mon Sep 17 00:00:00 2001 From: elamdf Date: Fri, 5 Apr 2024 12:00:21 -0700 Subject: [PATCH 22/68] hack around various issues related to the cadence pdk (no pwell/nwell/li1, clk gating cell disabled?) --- hammer/technology/sky130/__init__.py | 248 +++++++++++++++++---------- 1 file changed, 153 insertions(+), 95 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 9fdf223ed..34a4d6566 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -40,6 +40,8 @@ def gen_config(self) -> None: verilog_sim="cache/primitives.v", provides=[Provide(lib_type="technology")]), ] elif slib == "sky130_scl": + # Since the cadence (v0.0.3) tlef doesn't have nwell/pwell/li1, we can't use the sky130A IO libs. + # TODO: should we just take those layers out of the IO tlef? they shouldn't be necessary since we don't route on those layers libs += [ Library(lef_file="$SKY130_SCL/lef/sky130_scl_9T.tlef", verilog_sim="$SKY130_SCL/verilog/sky130_scl_9T.v", provides=[Provide(lib_type="technology")]), @@ -47,98 +49,100 @@ def gen_config(self) -> None: else: raise ValueError( f"Incorrect standard cell library selection: {slib}") - # Generate IO cells - library = 'sky130_fd_io' - SKYWATER_LIBS = os.path.join('$SKY130A', 'libs.ref', library) - LIBRARY_PATH = os.path.join(SKY130A, 'libs.ref', library, 'lib') - lib_corner_files = os.listdir(LIBRARY_PATH) - lib_corner_files.sort() - for cornerfilename in lib_corner_files: - # Skip versions with no internal power - if ('nointpwr' in cornerfilename): - continue - - tmp = cornerfilename.replace('.lib', '') - # Split into cell, and corner strings - # Resulting list if only one ff/ss/tt in name: [, , , , ] - # Resulting list if ff_ff/ss_ss/tt_tt in name: [, , , , '', , , , ] - split_cell_corner = re.split('_(ff)|_(ss)|_(tt)', tmp) - cell_name = split_cell_corner[0] - process = split_cell_corner[1:-1] - temp_volt = split_cell_corner[-1].split('_')[1:] - - # Filter out cross corners (e.g ff_ss or ss_ff) - if len(process) > 3: - if not functools.reduce(lambda x, y: x and y, map(lambda p, q: p == q, process[0:3], process[4:]), True): + # Generate IO cells unless we're using the Cadence stdcell lib (see above comment about missing layers in tlef) + if slib != "sky130_scl": + library = 'sky130_fd_io' + SKYWATER_LIBS = os.path.join('$SKY130A', 'libs.ref', library) + LIBRARY_PATH = os.path.join(SKY130A, 'libs.ref', library, 'lib') + lib_corner_files = os.listdir(LIBRARY_PATH) + lib_corner_files.sort() + for cornerfilename in lib_corner_files: + # Skip versions with no internal power + if ('nointpwr' in cornerfilename): continue - # Determine actual corner - speed = next(c for c in process if c is not None).replace('_', '') - if (speed == 'ff'): - speed = 'fast' - if (speed == 'tt'): - speed = 'typical' - if (speed == 'ss'): - speed = 'slow' - - temp = temp_volt[0] - temp = temp.replace('n', '-') - temp = temp.split('C')[0]+' C' - - vdd = ('.').join(temp_volt[1].split('v')) + ' V' - # Filter out IO/analog voltages that are not high voltage - if temp_volt[2].startswith('1'): - continue - if len(temp_volt) == 4: - if temp_volt[3].startswith('1'): + + tmp = cornerfilename.replace('.lib', '') + # Split into cell, and corner strings + # Resulting list if only one ff/ss/tt in name: [, , , , ] + # Resulting list if ff_ff/ss_ss/tt_tt in name: [, , , , '', , , , ] + split_cell_corner = re.split('_(ff)|_(ss)|_(tt)', tmp) + cell_name = split_cell_corner[0] + process = split_cell_corner[1:-1] + temp_volt = split_cell_corner[-1].split('_')[1:] + + # Filter out cross corners (e.g ff_ss or ss_ff) + if len(process) > 3: + if not functools.reduce(lambda x, y: x and y, map(lambda p, q: p == q, process[0:3], process[4:]), True): + continue + # Determine actual corner + speed = next(c for c in process if c is not None).replace('_', '') + if (speed == 'ff'): + speed = 'fast' + if (speed == 'tt'): + speed = 'typical' + if (speed == 'ss'): + speed = 'slow' + + temp = temp_volt[0] + temp = temp.replace('n', '-') + temp = temp.split('C')[0]+' C' + + vdd = ('.').join(temp_volt[1].split('v')) + ' V' + # Filter out IO/analog voltages that are not high voltage + if temp_volt[2].startswith('1'): continue + if len(temp_volt) == 4: + if temp_volt[3].startswith('1'): + continue + + # gpiov2_pad_wrapped has separate GDS + if cell_name == 'sky130_ef_io__gpiov2_pad_wrapped': + + file_lib = 'sky130_ef_io' + gds_file = cell_name + '.gds' + lef_file = os.path.join( + SKY130A, 'libs.ref', library, 'lef', "sky130_ef_io.lef") + spice_file = os.path.join( + SKYWATER_LIBS, 'cdl', file_lib + '.cdl') + elif 'sky130_ef_io' in cell_name: + file_lib = 'sky130_ef_io' + gds_file = file_lib + '.gds' + lef_file = os.path.join( + SKY130A, 'libs.ref', library, 'lef', "sky130_ef_io.lef") + spice_file = os.path.join( + SKYWATER_LIBS, 'cdl', file_lib + '.cdl') + else: + file_lib = library + gds_file = file_lib + '.gds' + lef_file = os.path.join( + SKYWATER_LIBS, 'lef', file_lib + '.lef') + spice_file = os.path.join( + SKYWATER_LIBS, 'spice', file_lib + '.spice') - # gpiov2_pad_wrapped has separate GDS - if cell_name == 'sky130_ef_io__gpiov2_pad_wrapped': - file_lib = 'sky130_ef_io' - gds_file = cell_name + '.gds' - lef_file = os.path.join( - SKY130A, 'libs.ref', library, 'lef', "sky130_ef_io.lef") - spice_file = os.path.join( - SKYWATER_LIBS, 'cdl', file_lib + '.cdl') - elif 'sky130_ef_io' in cell_name: - file_lib = 'sky130_ef_io' - gds_file = file_lib + '.gds' - lef_file = os.path.join( - SKY130A, 'libs.ref', library, 'lef', "sky130_ef_io.lef") - spice_file = os.path.join( - SKYWATER_LIBS, 'cdl', file_lib + '.cdl') - else: - file_lib = library - gds_file = file_lib + '.gds' - lef_file = os.path.join( - SKYWATER_LIBS, 'lef', file_lib + '.lef') - spice_file = os.path.join( - SKYWATER_LIBS, 'spice', file_lib + '.spice') - - lib_entry = Library( - nldm_liberty_file=os.path.join( - SKYWATER_LIBS, 'lib', cornerfilename), - verilog_sim=os.path.join( - SKYWATER_LIBS, 'verilog', file_lib + '.v'), - lef_file=lef_file, - spice_file=spice_file, - gds_file=os.path.join(SKYWATER_LIBS, 'gds', gds_file), - corner=Corner( - nmos=speed, - pmos=speed, - temperature=temp - ), - supplies=Supplies( - VDD=vdd, - GND="0 V" - ), - provides=[Provide( - lib_type=cell_name, - vt="RVT" + lib_entry = Library( + nldm_liberty_file=os.path.join( + SKYWATER_LIBS, 'lib', cornerfilename), + verilog_sim=os.path.join( + SKYWATER_LIBS, 'verilog', file_lib + '.v'), + lef_file=lef_file, + spice_file=spice_file, + gds_file=os.path.join(SKYWATER_LIBS, 'gds', gds_file), + corner=Corner( + nmos=speed, + pmos=speed, + temperature=temp + ), + supplies=Supplies( + VDD=vdd, + GND="0 V" + ), + provides=[Provide( + lib_type=cell_name, + vt="RVT" + ) + ] ) - ] - ) - libs.append(lib_entry) + libs.append(lib_entry) # Stdcell library-dependent lists stackups = [] # type: List[Stackup] @@ -182,6 +186,10 @@ def gen_config(self) -> None: # Generate standard cell library library = slib + + # scl vs 130a have different site names + sites = None + SKYWATER_LIBS = os.path.join('$SKY130A', 'libs.ref', library) LIBRARY_PATH = os.path.join(SKY130A, 'libs.ref', library, 'lib') lib_corner_files = os.listdir(LIBRARY_PATH) @@ -251,6 +259,11 @@ def gen_config(self) -> None: stackups.append( Stackup(name=slib, grid_unit=Decimal("0.001"), metals=metals)) + sites = [ + Site(name="unithd", x=Decimal("0.46"), y=Decimal("2.72")), + Site(name="unithddbl", x=Decimal("0.46"), y=Decimal("5.44")) + ] + elif slib == "sky130_scl": # The cadence PDK (as of version 0.0.3) doesn't seem to have tap nor decap cells, so par won't run (and if we forced it to, lvs would fail) @@ -262,7 +275,7 @@ def gen_config(self) -> None: SpecialCell(cell_type="ctsbuffer", name=["CLKBUFX2"]), # NOTE: this cell is marked don't use/touch in v0.0.3 of the cadence stdcell library. we're bypassing this with `enable_scl_clk_gating_cell`. SpecialCell(cell_type=CellType("ctsgate"), - name=["ICGX1"]) + name=["ICGX1"]), ] # Generate standard cell library @@ -309,7 +322,7 @@ def gen_config(self) -> None: lef_file=os.path.join(SKY130_CDS_LIB, 'lef', library+'_9T.lef'), spice_file=os.path.join( 'cache', library+'.cdl'), - gds_file=os.path.join(SKY130_CDS, 'gds', library+'_9T.gds'), + gds_file=os.path.join(SKY130_CDS_LIB, 'gds', library+'_9T.gds'), corner=Corner( nmos=speed, pmos=speed, @@ -337,6 +350,12 @@ def gen_config(self) -> None: stackups.append( Stackup(name=slib, grid_unit=Decimal("0.001"), metals=metals)) + sites = [ + Site(name="CoreSite", x=Decimal("0.46"), y=Decimal("4.14")), + Site(name="IOSite", x=Decimal("1.0"), y=Decimal("240.0")), + Site(name="CornerSite", x=Decimal("240.0"), y=Decimal("240.0")) + ] + else: raise ValueError( f"Incorrect standard cell library selection: {slib}") @@ -375,10 +394,7 @@ def gen_config(self) -> None: ], additional_lvs_text="", tarballs=None, - sites=[ - Site(name="unithd", x=Decimal("0.46"), y=Decimal("2.72")), - Site(name="unithddbl", x=Decimal("0.46"), y=Decimal("5.44")) - ], + sites=sites, stackups=stackups, special_cells=spcl_cells, extra_prefixes=None @@ -557,10 +573,16 @@ def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]: hooks = { "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_connect_nets), HammerTool.make_pre_insertion_hook( "write_design", sky130_connect_nets2) ]} + # there are no cap/decap cells in the cadence stdcell library as of version 0.0.3, so we can't do things that reference them + if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": + hooks['innovus'].append(HammerTool.make_replacement_hook("power_straps", power_straps_no_tapcells)) + else: + hooks['innovus'].append(HammerTool.make_pre_insertion_hook( "place_tap_cells", sky130_add_endcaps)) + + return hooks.get(tool_name, []) def get_tech_drc_hooks(self, tool_name: str) -> List[HammerToolHookAction]: @@ -759,6 +781,42 @@ def efabless_ring_io(ht: HammerTool) -> bool: ''') return True +def power_straps_no_tapcells(ht: HammerTool) -> bool: + ht.append(''' +# -------------------------------------------------------------------------------- +# This script was written and developed by HAMMER at UC Berkeley; however, the +# underlying commands and reports are copyrighted by Cadence. We thank Cadence for +# granting permission to share our research to help promote and foster the next +# generation of innovators. +# -------------------------------------------------------------------------------- + +# Power strap definition for layer met1 (rails): + +set_db add_stripes_stacked_via_bottom_layer met1 +set_db add_stripes_stacked_via_top_layer met1 +set_db add_stripes_spacing_from_block 2.000 +# Provide some stdcells to use as reference because the Cadence (v0.0.3) pdk doesn't have tapcells +add_stripes -pin_layer met1 -layer met1 -over_pins 1 -master SDFF* -block_ring_bottom_layer_limit met1 -block_ring_top_layer_limit met1 -pad_core_ring_bottom_layer_limit met1 -pad_core_ring_top_layer_limit met1 -direction horizontal -width pin_width -nets { VSS VDD } + +# Power strap definition for layer met4: + +set_db add_stripes_stacked_via_top_layer met4 +set_db add_stripes_stacked_via_bottom_layer met1 +set_db add_stripes_trim_antenna_back_to_shape {stripe} +set_db add_stripes_spacing_from_block 2.000 +add_stripes -create_pins 0 -block_ring_bottom_layer_limit met4 -block_ring_top_layer_limit met1 -direction vertical -layer met4 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met1 -set_to_set_distance 75.90 -spacing 3.66 -switch_layer_over_obs 0 -width 1.86 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 0] + 7.35] + +# Power strap definition for layer met5: + +set_db add_stripes_stacked_via_top_layer met5 +set_db add_stripes_stacked_via_bottom_layer met4 +set_db add_stripes_trim_antenna_back_to_shape {stripe} +set_db add_stripes_spacing_from_block 2.000 +add_stripes -create_pins 1 -block_ring_bottom_layer_limit met5 -block_ring_top_layer_limit met4 -direction horizontal -layer met5 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met4 -set_to_set_distance 225.40 -spacing 17.68 -switch_layer_over_obs 0 -width 1.64 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 1] + 5.62] +''') + return True + + def calibre_drc_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerDRCTool), "Exlude SRAMs only in DRC" drc_box = '' From 6554d917f4764ea072bc01d48451de78e6301011 Mon Sep 17 00:00:00 2001 From: elamdf Date: Mon, 6 May 2024 14:52:27 -0700 Subject: [PATCH 23/68] make power-rtl work with joules --- hammer/technology/sky130/__init__.py | 30 ++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 34a4d6566..27d1e2932 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -32,7 +32,7 @@ def gen_config(self) -> None: SKY130_CDS_LIB = self.get_setting("technology.sky130.sky130_scl") # Common tech LEF and IO cell spice netlists - libs = [Library(spice_file="$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io__analog.spice", + libs = [Library(spice_file="$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io.spice", provides=[Provide(lib_type="IO library")])] if slib == "sky130_fd_sc_hd": libs += [ @@ -317,11 +317,10 @@ def gen_config(self) -> None: lib_entry = Library( nldm_liberty_file=os.path.join( SKY130_CDS_LIB, 'lib', cornerfilename), - verilog_sim=os.path.join( - 'cache', library+'.v'), + verilog_sim=os.path.join(SKY130_CDS_LIB, 'verilog', library+'_9T.v'), lef_file=os.path.join(SKY130_CDS_LIB, 'lef', library+'_9T.lef'), spice_file=os.path.join( - 'cache', library+'.cdl'), + SKY130_CDS_LIB, 'cdl', library+'_9T.cdl'), gds_file=os.path.join(SKY130_CDS_LIB, 'gds', library+'_9T.gds'), corner=Corner( nmos=speed, @@ -556,16 +555,35 @@ def setup_techlef(self) -> None: df.write(line) if line.strip() == 'END pwell': df.write(_the_tlef_edit) + + + + # syn_power seems to not take hooks from `get_tech_syn_hooks` + # also, syn_power is called from joules so we need to add the hook to joules + # TODO: clean this up, there should be a way to always include this hook whenever genus will be invoked + def get_tech_power_hooks(self, tool_name: str) -> List[HammerToolHookAction]: + hooks = {} + def enable_scl_clk_gating_cell_hook(ht: HammerTool) -> bool: + ht.append("set_db [get_db lib_cells -if {.base_name == ICGX1}] .avoid false") + return True + + + # The clock gating cell is set to don't touch/use in the cadence pdk (as of v0.0.3), work around that + if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": + hooks["joules"] = [HammerTool.make_pre_insertion_hook("synthesize_design", enable_scl_clk_gating_cell_hook)] + + return hooks.get(tool_name, []) + def get_tech_syn_hooks(self, tool_name: str) -> List[HammerToolHookAction]: hooks = {} - def enable_scl_clk_gating_cell(ht: HammerTool) -> bool: + def enable_scl_clk_gating_cell_hook(ht: HammerTool) -> bool: ht.append("set_db [get_db lib_cells -if {.base_name == ICGX1}] .avoid false") return True # The clock gating cell is set to don't touch/use in the cadence pdk (as of v0.0.3), work around that if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": - hooks["genus"] = [HammerTool.make_pre_insertion_hook("syn_generic", enable_scl_clk_gating_cell)] + hooks["genus"] = [HammerTool.make_pre_insertion_hook("syn_generic", enable_scl_clk_gating_cell_hook)] return hooks.get(tool_name, []) From 1066c89a4942d373314582316e9561d86bcef26f Mon Sep 17 00:00:00 2001 From: elamdf Date: Mon, 8 Jul 2024 01:55:43 -0700 Subject: [PATCH 24/68] working on lvs, par still not 100% clean --- hammer/technology/sky130/__init__.py | 69 +++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 27d1e2932..11b7287a8 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -32,11 +32,12 @@ def gen_config(self) -> None: SKY130_CDS_LIB = self.get_setting("technology.sky130.sky130_scl") # Common tech LEF and IO cell spice netlists - libs = [Library(spice_file="$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io.spice", - provides=[Provide(lib_type="IO library")])] + libs = [] if slib == "sky130_fd_sc_hd": libs += [ - Library(lef_file="$SKY130A/sky130_fd_sc_hd__nom.tlef", + Library(spice_file="$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io.spice", + provides=[Provide(lib_type="IO library")]), + Library(lef_file="cache/sky130_fd_sc_hd__nom.tlef", verilog_sim="cache/primitives.v", provides=[Provide(lib_type="technology")]), ] elif slib == "sky130_scl": @@ -263,6 +264,12 @@ def gen_config(self) -> None: Site(name="unithd", x=Decimal("0.46"), y=Decimal("2.72")), Site(name="unithddbl", x=Decimal("0.46"), y=Decimal("5.44")) ] + lvs_decks=[ + LVSDeck(tool_name="calibre", deck_name="calibre_lvs", + path="$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8"), + LVSDeck(tool_name="pegasus", deck_name="pegasus_lvs", + path="$SKY130_CDS/Sky130_LVS/Sky130_rev_0.0_0.1.lvs.pvl") + ] elif slib == "sky130_scl": @@ -355,6 +362,14 @@ def gen_config(self) -> None: Site(name="CornerSite", x=Decimal("240.0"), y=Decimal("240.0")) ] + lvs_decks=[ + #LVSDeck(tool_name="calibre", deck_name="calibre_lvs", + #path="$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8"), + # seems like cadence just hasthis hardcoded lvs deck name? + LVSDeck(tool_name="pegasus", deck_name="pegasus_lvs", + path=os.path.join(SKY130_CDS, 'Sky130_LVS', 'sky130.lvs.pvl')) + ] + else: raise ValueError( f"Incorrect standard cell library selection: {slib}") @@ -385,12 +400,7 @@ def gen_config(self) -> None: path="$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl") ], additional_drc_text="", - lvs_decks=[ - LVSDeck(tool_name="calibre", deck_name="calibre_lvs", - path="$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8"), - LVSDeck(tool_name="pegasus", deck_name="pegasus_lvs", - path="$SKY130_CDS/Sky130_LVS/Sky130_rev_0.0_0.1.lvs.pvl") - ], + lvs_decks=lvs_decks, additional_lvs_text="", tarballs=None, sites=sites, @@ -405,7 +415,7 @@ def post_install_script(self) -> None: self.use_sram22 = os.path.exists(self.get_setting("technology.sky130.sram22_sky130_macros")) if self.get_setting("technology.sky130.stdcell_library") == "sky130_fd_sc_hd": self.setup_cdl() - self.setup_verilog() + #self.setup_verilog() self.setup_techlef() self.logger.info('Loaded Sky130 Tech') @@ -621,7 +631,9 @@ def get_tech_lvs_hooks(self, tool_name: str) -> List[HammerToolHookAction]: calibre_hooks.append(HammerTool.make_post_insertion_hook("generate_lvs_run_file", sram22_lvs_recognize_gates_all)) if self.get_setting("technology.sky130.lvs_blackbox_srams"): calibre_hooks.append(HammerTool.make_post_insertion_hook("generate_lvs_run_file", calibre_lvs_blackbox_srams)) - pegasus_hooks.append(HammerTool.make_post_insertion_hook("generate_lvs_ctl_file", pegasus_lvs_blackbox_srams)) + + if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": + pegasus_hooks.append(HammerTool.make_post_insertion_hook("generate_lvs_ctl_file", pegasus_lvs_add_130a_primitives)) hooks = {"calibre": calibre_hooks, "pegasus": pegasus_hooks } @@ -636,6 +648,17 @@ def openram_sram_names() -> List[str]: "sky130_sram_2kbyte_1rw1r_32x512_8" ] + @staticmethod + def sky130_sram_primitive_names() -> List[str]: + # TODO: fix this + spice_filenames = ["sky130_fd_pr__pfet_01v8", "sky130_fd_pr__nfet_01v8", "sky130_fd_pr__pfet_01v8_hvt", "sky130_fd_pr__special_nfet_latch", "sky130_fd_pr__special_nfet_pass"] #"sky130_fd_pr__special_pfet_latch", ] + paths = [] + for fname in spice_filenames: + paths.append(f"/tools/commercial/skywater/local/sky130A/libs.ref/sky130_fd_pr/spice/{fname}.pm3.spice") + # TODO: this is bc line 535 in the bwrc one causes a syntax error + paths.append("/tools/C/elamdf/chipyard_dev/vlsi/sky130_fd_pr__special_pfet_latch.pm3.spice") + return paths + @staticmethod def sky130_sram_names() -> List[str]: sky130_sram_names = [] @@ -866,10 +889,32 @@ def calibre_lvs_blackbox_srams(ht: HammerTool) -> bool: f.write(lvs_box) return True +# required for sram22 since they use the 130a primiviites +def pegasus_lvs_add_130a_primitives(ht: HammerTool) -> bool: + assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" + lvs_box = '' + for name in SKY130Tech.sky130_sram_primitive_names(): + lvs_box += f"""\nschematic_path "{name}" spice;""" + # this is because otherwise lvs crashes with tons of stdcell-level pin mismatches + # TODO elam move this somewhere better + #lvs_box += f"""\nlvs_discard_pins yes;""" + #lvs_box += f"""\nlvs_expand_cell_on_error yes;""" + lvs_box += f"""\nlvs_inconsistent_reduction_threshold -none;""" + run_file = ht.lvs_ctl_file # type: ignore + with open(run_file, "r+") as f: + # Remove SRAM SPICE file includes. + pattern = 'schematic_path.*({}).*spice;\n'.format('|'.join(SKY130Tech.sky130_sram_primitive_names())) + matcher = re.compile(pattern) + contents = f.read() + fixed_contents = contents + lvs_box + f.seek(0) + f.write(fixed_contents) + return True + def pegasus_lvs_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" lvs_box = '' - for name in SKY130Tech.sky130_sram_names(): + for name in SKY130Tech.sky130_sram_names() + SKY130Tech.sky130_sram_primitive_names(): lvs_box += f"\nlvs_black_box {name} -gray" run_file = ht.lvs_ctl_file # type: ignore with open(run_file, "r+") as f: From 7e5adfe85d57254fc584bdc16100bd11034fe46c Mon Sep 17 00:00:00 2001 From: elamdf Date: Tue, 27 Aug 2024 02:38:26 -0700 Subject: [PATCH 25/68] formatting + [TODO test lvs on more designs] add hardcoded power straps hook since we don't have tapcells to reference for rails. This is lvs clean for the sky130-commercial-tutorial (but there are no srams? unsure why) --- hammer/technology/sky130/__init__.py | 954 ++++++++++++++++----------- 1 file changed, 587 insertions(+), 367 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 11b7287a8..82988a26f 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -13,9 +13,17 @@ import functools from hammer.tech import * -from hammer.vlsi import HammerTool, HammerPlaceAndRouteTool, TCLTool, HammerDRCTool, HammerLVSTool, \ - HammerToolHookAction, HierarchicalMode +from hammer.vlsi import ( + HammerTool, + HammerPlaceAndRouteTool, + TCLTool, + HammerDRCTool, + HammerLVSTool, + HammerToolHookAction, + HierarchicalMode, +) from hammer.utils import LEFUtils +from hammer.vlsi.hammer_vlsi_impl import HammerSimTool class SKY130Tech(HammerTechnology): @@ -35,113 +43,113 @@ def gen_config(self) -> None: libs = [] if slib == "sky130_fd_sc_hd": libs += [ - Library(spice_file="$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io.spice", - provides=[Provide(lib_type="IO library")]), - Library(lef_file="cache/sky130_fd_sc_hd__nom.tlef", - verilog_sim="cache/primitives.v", provides=[Provide(lib_type="technology")]), + Library( + spice_file="$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io.spice", + provides=[Provide(lib_type="IO library")], + ), + Library( + lef_file="cache/sky130_fd_sc_hd__nom.tlef", + verilog_sim="cache/primitives.v", + provides=[Provide(lib_type="technology")], + ), ] elif slib == "sky130_scl": - # Since the cadence (v0.0.3) tlef doesn't have nwell/pwell/li1, we can't use the sky130A IO libs. - # TODO: should we just take those layers out of the IO tlef? they shouldn't be necessary since we don't route on those layers + # Since the cadence (v0.0.3) tlef doesn't have nwell/pwell/li1, we can't use the sky130A IO libs. + # TODO: should we just take those layers out of the IO tlef? they shouldn't be necessary since we don't route on those layers libs += [ - Library(lef_file="$SKY130_SCL/lef/sky130_scl_9T.tlef", - verilog_sim="$SKY130_SCL/verilog/sky130_scl_9T.v", provides=[Provide(lib_type="technology")]), + Library( + lef_file="$SKY130_SCL/lef/sky130_scl_9T.tlef", + verilog_sim="$SKY130_SCL/verilog/sky130_scl_9T.v", + provides=[Provide(lib_type="technology")], + ), ] else: - raise ValueError( - f"Incorrect standard cell library selection: {slib}") + raise ValueError(f"Incorrect standard cell library selection: {slib}") # Generate IO cells unless we're using the Cadence stdcell lib (see above comment about missing layers in tlef) - if slib != "sky130_scl": - library = 'sky130_fd_io' - SKYWATER_LIBS = os.path.join('$SKY130A', 'libs.ref', library) - LIBRARY_PATH = os.path.join(SKY130A, 'libs.ref', library, 'lib') + if slib != "sky130_scl": + library = "sky130_fd_io" + SKYWATER_LIBS = os.path.join("$SKY130A", "libs.ref", library) + LIBRARY_PATH = os.path.join(SKY130A, "libs.ref", library, "lib") lib_corner_files = os.listdir(LIBRARY_PATH) lib_corner_files.sort() for cornerfilename in lib_corner_files: # Skip versions with no internal power - if ('nointpwr' in cornerfilename): + if "nointpwr" in cornerfilename: continue - tmp = cornerfilename.replace('.lib', '') + tmp = cornerfilename.replace(".lib", "") # Split into cell, and corner strings # Resulting list if only one ff/ss/tt in name: [, , , , ] # Resulting list if ff_ff/ss_ss/tt_tt in name: [, , , , '', , , , ] - split_cell_corner = re.split('_(ff)|_(ss)|_(tt)', tmp) + split_cell_corner = re.split("_(ff)|_(ss)|_(tt)", tmp) cell_name = split_cell_corner[0] process = split_cell_corner[1:-1] - temp_volt = split_cell_corner[-1].split('_')[1:] + temp_volt = split_cell_corner[-1].split("_")[1:] # Filter out cross corners (e.g ff_ss or ss_ff) if len(process) > 3: - if not functools.reduce(lambda x, y: x and y, map(lambda p, q: p == q, process[0:3], process[4:]), True): + if not functools.reduce( + lambda x, y: x and y, + map(lambda p, q: p == q, process[0:3], process[4:]), + True, + ): continue # Determine actual corner - speed = next(c for c in process if c is not None).replace('_', '') - if (speed == 'ff'): - speed = 'fast' - if (speed == 'tt'): - speed = 'typical' - if (speed == 'ss'): - speed = 'slow' + speed = next(c for c in process if c is not None).replace("_", "") + if speed == "ff": + speed = "fast" + if speed == "tt": + speed = "typical" + if speed == "ss": + speed = "slow" temp = temp_volt[0] - temp = temp.replace('n', '-') - temp = temp.split('C')[0]+' C' + temp = temp.replace("n", "-") + temp = temp.split("C")[0] + " C" - vdd = ('.').join(temp_volt[1].split('v')) + ' V' + vdd = (".").join(temp_volt[1].split("v")) + " V" # Filter out IO/analog voltages that are not high voltage - if temp_volt[2].startswith('1'): + if temp_volt[2].startswith("1"): continue if len(temp_volt) == 4: - if temp_volt[3].startswith('1'): + if temp_volt[3].startswith("1"): continue # gpiov2_pad_wrapped has separate GDS - if cell_name == 'sky130_ef_io__gpiov2_pad_wrapped': + if cell_name == "sky130_ef_io__gpiov2_pad_wrapped": - file_lib = 'sky130_ef_io' - gds_file = cell_name + '.gds' + file_lib = "sky130_ef_io" + gds_file = cell_name + ".gds" lef_file = os.path.join( - SKY130A, 'libs.ref', library, 'lef', "sky130_ef_io.lef") - spice_file = os.path.join( - SKYWATER_LIBS, 'cdl', file_lib + '.cdl') - elif 'sky130_ef_io' in cell_name: - file_lib = 'sky130_ef_io' - gds_file = file_lib + '.gds' + SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" + ) + spice_file = os.path.join(SKYWATER_LIBS, "cdl", file_lib + ".cdl") + elif "sky130_ef_io" in cell_name: + file_lib = "sky130_ef_io" + gds_file = file_lib + ".gds" lef_file = os.path.join( - SKY130A, 'libs.ref', library, 'lef', "sky130_ef_io.lef") - spice_file = os.path.join( - SKYWATER_LIBS, 'cdl', file_lib + '.cdl') + SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" + ) + spice_file = os.path.join(SKYWATER_LIBS, "cdl", file_lib + ".cdl") else: file_lib = library - gds_file = file_lib + '.gds' - lef_file = os.path.join( - SKYWATER_LIBS, 'lef', file_lib + '.lef') + gds_file = file_lib + ".gds" + lef_file = os.path.join(SKYWATER_LIBS, "lef", file_lib + ".lef") spice_file = os.path.join( - SKYWATER_LIBS, 'spice', file_lib + '.spice') + SKYWATER_LIBS, "spice", file_lib + ".spice" + ) lib_entry = Library( nldm_liberty_file=os.path.join( - SKYWATER_LIBS, 'lib', cornerfilename), - verilog_sim=os.path.join( - SKYWATER_LIBS, 'verilog', file_lib + '.v'), + SKYWATER_LIBS, "lib", cornerfilename + ), + verilog_sim=os.path.join(SKYWATER_LIBS, "verilog", file_lib + ".v"), lef_file=lef_file, spice_file=spice_file, - gds_file=os.path.join(SKYWATER_LIBS, 'gds', gds_file), - corner=Corner( - nmos=speed, - pmos=speed, - temperature=temp - ), - supplies=Supplies( - VDD=vdd, - GND="0 V" - ), - provides=[Provide( - lib_type=cell_name, - vt="RVT" - ) - ] + gds_file=os.path.join(SKYWATER_LIBS, "gds", gds_file), + corner=Corner(nmos=speed, pmos=speed, temperature=temp), + supplies=Supplies(VDD=vdd, GND="0 V"), + provides=[Provide(lib_type=cell_name, vt="RVT")], ) libs.append(lib_entry) @@ -155,194 +163,234 @@ def gen_config(self) -> None: if slib == "sky130_fd_sc_hd": phys_only = [ - "sky130_fd_sc_hd__tap_1", "sky130_fd_sc_hd__tap_2", "sky130_fd_sc_hd__tapvgnd_1", "sky130_fd_sc_hd__tapvpwrvgnd_1", - "sky130_fd_sc_hd__fill_1", "sky130_fd_sc_hd__fill_2", "sky130_fd_sc_hd__fill_4", "sky130_fd_sc_hd__fill_8", - "sky130_fd_sc_hd__diode_2"] + "sky130_fd_sc_hd__tap_1", + "sky130_fd_sc_hd__tap_2", + "sky130_fd_sc_hd__tapvgnd_1", + "sky130_fd_sc_hd__tapvpwrvgnd_1", + "sky130_fd_sc_hd__fill_1", + "sky130_fd_sc_hd__fill_2", + "sky130_fd_sc_hd__fill_4", + "sky130_fd_sc_hd__fill_8", + "sky130_fd_sc_hd__diode_2", + ] dont_use = [ "*sdf*", "sky130_fd_sc_hd__probe_p_*", - "sky130_fd_sc_hd__probec_p_*" + "sky130_fd_sc_hd__probec_p_*", ] spcl_cells = [ - SpecialCell(cell_type=CellType("tiehilocell"), - name=["sky130_fd_sc_hd__conb_1"]), - SpecialCell(cell_type=CellType("tiehicell"), name=[ - "sky130_fd_sc_hd__conb_1"], output_ports=["HI"]), - SpecialCell(cell_type=CellType("tielocell"), name=[ - "sky130_fd_sc_hd__conb_1"], output_ports=["LO"]), - SpecialCell(cell_type=CellType("endcap"), - name=["sky130_fd_sc_hd__tap_1"]), - SpecialCell(cell_type=CellType("tapcell"), name=[ - "sky130_fd_sc_hd__tapvpwrvgnd_1"]), - SpecialCell(cell_type=CellType("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"]), - SpecialCell(cell_type=CellType("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"]), - SpecialCell(cell_type=CellType("driver"), name=[ - "sky130_fd_sc_hd__buf_4"], input_ports=["A"], output_ports=["X"]), - SpecialCell(cell_type=CellType("ctsbuffer"), - name=["sky130_fd_sc_hd__clkbuf_1"]) + # for now, skipping the tiecell step. I think the extracted verilog netlist is ignoring the tiecells which is causing lvs issues + SpecialCell( + cell_type=CellType("tiehilocell"), name=["sky130_fd_sc_hd__conb_1"] + ), + SpecialCell( + cell_type=CellType("tiehicell"), + name=["sky130_fd_sc_hd__conb_1"], + output_ports=["HI"], + ), + SpecialCell( + cell_type=CellType("tielocell"), + name=["sky130_fd_sc_hd__conb_1"], + output_ports=["LO"], + ), + SpecialCell( + cell_type=CellType("endcap"), name=["sky130_fd_sc_hd__tap_1"] + ), + SpecialCell( + cell_type=CellType("tapcell"), + name=["sky130_fd_sc_hd__tapvpwrvgnd_1"], + ), + SpecialCell( + cell_type=CellType("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", + ], + ), + SpecialCell( + cell_type=CellType("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", + ], + ), + SpecialCell( + cell_type=CellType("driver"), + name=["sky130_fd_sc_hd__buf_4"], + input_ports=["A"], + output_ports=["X"], + ), + SpecialCell( + cell_type=CellType("ctsbuffer"), name=["sky130_fd_sc_hd__clkbuf_1"] + ), ] # Generate standard cell library library = slib - # scl vs 130a have different site names sites = None - SKYWATER_LIBS = os.path.join('$SKY130A', 'libs.ref', library) - LIBRARY_PATH = os.path.join(SKY130A, 'libs.ref', library, 'lib') + SKYWATER_LIBS = os.path.join("$SKY130A", "libs.ref", library) + LIBRARY_PATH = os.path.join(SKY130A, "libs.ref", library, "lib") lib_corner_files = os.listdir(LIBRARY_PATH) lib_corner_files.sort() for cornerfilename in lib_corner_files: - if (not ("sky130" in cornerfilename)): # cadence doesn't use the lib name in their corner libs + if not ( + "sky130" in cornerfilename + ): # cadence doesn't use the lib name in their corner libs continue - if ('ccsnoise' in cornerfilename): + if "ccsnoise" in cornerfilename: continue # ignore duplicate corner.lib/corner_ccsnoise.lib files - tmp = cornerfilename.replace('.lib', '') - if (tmp+'_ccsnoise.lib' in lib_corner_files): - cornerfilename = tmp+'_ccsnoise.lib' # use ccsnoise version of lib file + tmp = cornerfilename.replace(".lib", "") + if tmp + "_ccsnoise.lib" in lib_corner_files: + cornerfilename = ( + tmp + "_ccsnoise.lib" + ) # use ccsnoise version of lib file - cornername = tmp.split('__')[1] - cornerparts = cornername.split('_') + cornername = tmp.split("__")[1] + cornerparts = cornername.split("_") speed = cornerparts[0] - if (speed == 'ff'): - speed = 'fast' - if (speed == 'tt'): - speed = 'typical' - if (speed == 'ss'): - speed = 'slow' + if speed == "ff": + speed = "fast" + if speed == "tt": + speed = "typical" + if speed == "ss": + speed = "slow" temp = cornerparts[1] - temp = temp.replace('n', '-') - temp = temp.split('C')[0]+' C' + temp = temp.replace("n", "-") + temp = temp.split("C")[0] + " C" vdd = cornerparts[2] - vdd = vdd.split('v')[0]+'.'+vdd.split('v')[1]+' V' + vdd = vdd.split("v")[0] + "." + vdd.split("v")[1] + " V" lib_entry = Library( nldm_liberty_file=os.path.join( - SKYWATER_LIBS, 'lib', cornerfilename), - verilog_sim=os.path.join( - 'cache', library+'.v'), - lef_file=os.path.join( - SKYWATER_LIBS, 'lef', library+'.lef'), - spice_file=os.path.join( - 'cache', library+'.cdl'), - gds_file=os.path.join( - SKYWATER_LIBS, 'gds', library+'.gds'), - corner=Corner( - nmos=speed, - pmos=speed, - temperature=temp - ), - supplies=Supplies( - VDD=vdd, - GND="0 V" + SKYWATER_LIBS, "lib", cornerfilename ), - provides=[Provide( - lib_type="stdcell", - vt="RVT" - ) - ] + verilog_sim=os.path.join("cache", library + ".v"), + lef_file=os.path.join(SKYWATER_LIBS, "lef", library + ".lef"), + spice_file=os.path.join("cache", library + ".cdl"), + gds_file=os.path.join(SKYWATER_LIBS, "gds", library + ".gds"), + corner=Corner(nmos=speed, pmos=speed, temperature=temp), + supplies=Supplies(VDD=vdd, GND="0 V"), + provides=[Provide(lib_type="stdcell", vt="RVT")], ) libs.append(lib_entry) # Generate stackup tlef_path = os.path.join( - SKY130A, 'libs.ref', library, 'techlef', f"{library}__min.tlef") - metals = list(map(lambda m: Metal.model_validate(m), - LEFUtils.get_metals(tlef_path))) + SKY130A, "libs.ref", library, "techlef", f"{library}__min.tlef" + ) + metals = list( + map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path)) + ) stackups.append( - Stackup(name=slib, grid_unit=Decimal("0.001"), metals=metals)) + Stackup(name=slib, grid_unit=Decimal("0.001"), metals=metals) + ) sites = [ Site(name="unithd", x=Decimal("0.46"), y=Decimal("2.72")), - Site(name="unithddbl", x=Decimal("0.46"), y=Decimal("5.44")) + Site(name="unithddbl", x=Decimal("0.46"), y=Decimal("5.44")), ] - lvs_decks=[ - LVSDeck(tool_name="calibre", deck_name="calibre_lvs", - path="$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8"), - LVSDeck(tool_name="pegasus", deck_name="pegasus_lvs", - path="$SKY130_CDS/Sky130_LVS/Sky130_rev_0.0_0.1.lvs.pvl") + lvs_decks = [ + LVSDeck( + tool_name="calibre", + deck_name="calibre_lvs", + path="$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8", + ), + LVSDeck( + tool_name="pegasus", + deck_name="pegasus_lvs", + path="$SKY130_CDS/Sky130_LVS/sky130.lvs.pvl", + ), ] elif slib == "sky130_scl": # The cadence PDK (as of version 0.0.3) doesn't seem to have tap nor decap cells, so par won't run (and if we forced it to, lvs would fail) spcl_cells = [ - SpecialCell(cell_type="stdfiller", name= - [f"FILL{i**2}" for i in range(7)]), - SpecialCell(cell_type="driver", name=[ - "TBUF"], input_ports=["A"], output_ports=["Y"]), + SpecialCell( + cell_type="stdfiller", name=[f"FILL{i**2}" for i in range(7)] + ), + SpecialCell( + cell_type="driver", + name=["TBUF"], + input_ports=["A"], + output_ports=["Y"], + ), SpecialCell(cell_type="ctsbuffer", name=["CLKBUFX2"]), - # NOTE: this cell is marked don't use/touch in v0.0.3 of the cadence stdcell library. we're bypassing this with `enable_scl_clk_gating_cell`. - SpecialCell(cell_type=CellType("ctsgate"), - name=["ICGX1"]), + SpecialCell(cell_type=CellType("ctsgate"), name=["ICGX1"]), + SpecialCell( + cell_type=CellType("tiehicell"), name=["TIEHI"], input_ports=["Y"] + ), + SpecialCell( + cell_type=CellType("tielocell"), name=["TIELO"], input_ports=["Y"] + ), ] # Generate standard cell library library = slib - LIBRARY_PATH = os.path.join(SKY130_CDS_LIB, 'lib') + LIBRARY_PATH = os.path.join(SKY130_CDS_LIB, "lib") lib_corner_files = os.listdir(LIBRARY_PATH) lib_corner_files.sort() for cornerfilename in lib_corner_files: - if (not ("sky130" in cornerfilename)): # cadence doesn't use the lib name in their corner libs + if not ( + "sky130" in cornerfilename + ): # cadence doesn't use the lib name in their corner libs continue - if ('ccsnoise' in cornerfilename): + if "ccsnoise" in cornerfilename: continue # ignore duplicate corner.lib/corner_ccsnoise.lib files - tmp = cornerfilename.replace('.lib', '') - if (tmp+'_ccsnoise.lib' in lib_corner_files): - cornerfilename = tmp+'_ccsnoise.lib' # use ccsnoise version of lib file + tmp = cornerfilename.replace(".lib", "") + if tmp + "_ccsnoise.lib" in lib_corner_files: + cornerfilename = ( + tmp + "_ccsnoise.lib" + ) # use ccsnoise version of lib file cornername = tmp.replace("sky130_", "") - cornerparts = cornername.split('_') + cornerparts = cornername.split("_") # Hardcode corner annotations since they don't exactly match the sky130a speed = cornerparts[0] vdd = "" temp = "" - if (speed == 'ff'): + if speed == "ff": temp = "-40 C" vdd = "1.95 V" - speed = 'fast' - if (speed == 'tt'): + speed = "fast" + if speed == "tt": vdd = "1.80 V" temp = "25 C" - speed = 'typical' - if (speed == 'ss'): + speed = "typical" + if speed == "ss": vdd = "1.60 V" - speed = 'slow' + speed = "slow" temp = "100 C" lib_entry = Library( nldm_liberty_file=os.path.join( - SKY130_CDS_LIB, 'lib', cornerfilename), - verilog_sim=os.path.join(SKY130_CDS_LIB, 'verilog', library+'_9T.v'), - lef_file=os.path.join(SKY130_CDS_LIB, 'lef', library+'_9T.lef'), - spice_file=os.path.join( - SKY130_CDS_LIB, 'cdl', library+'_9T.cdl'), - gds_file=os.path.join(SKY130_CDS_LIB, 'gds', library+'_9T.gds'), - corner=Corner( - nmos=speed, - pmos=speed, - temperature=temp + SKY130_CDS_LIB, "lib", cornerfilename ), - supplies=Supplies( - VDD=vdd, - GND="0 V" + verilog_sim=os.path.join( + SKY130_CDS_LIB, "verilog", library + "_9T.v" ), - provides=[Provide( - lib_type="stdcell", - vt="RVT" - ) - ] + lef_file=os.path.join(SKY130_CDS_LIB, "lef", library + "_9T.lef"), + spice_file=os.path.join(SKY130_CDS_LIB, "cdl", library + "_9T.cdl"), + gds_file=os.path.join(SKY130_CDS_LIB, "gds", library + "_9T.gds"), + corner=Corner(nmos=speed, pmos=speed, temperature=temp), + supplies=Supplies(VDD=vdd, GND="0 V"), + provides=[Provide(lib_type="stdcell", vt="RVT")], ) libs.append(lib_entry) @@ -350,54 +398,64 @@ def gen_config(self) -> None: # Generate stackup metals = [] # type: List[Metal] - tlef_path = os.path.join(SKY130_CDS_LIB, 'lef', f"{slib}_9T.tlef") - metals = list(map(lambda m: Metal.model_validate(m), - LEFUtils.get_metals(tlef_path))) + tlef_path = os.path.join(SKY130_CDS_LIB, "lef", f"{slib}_9T.tlef") + metals = list( + map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path)) + ) stackups.append( - Stackup(name=slib, grid_unit=Decimal("0.001"), metals=metals)) + Stackup(name=slib, grid_unit=Decimal("0.001"), metals=metals) + ) sites = [ Site(name="CoreSite", x=Decimal("0.46"), y=Decimal("4.14")), Site(name="IOSite", x=Decimal("1.0"), y=Decimal("240.0")), - Site(name="CornerSite", x=Decimal("240.0"), y=Decimal("240.0")) + Site(name="CornerSite", x=Decimal("240.0"), y=Decimal("240.0")), ] - lvs_decks=[ - #LVSDeck(tool_name="calibre", deck_name="calibre_lvs", - #path="$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8"), - # seems like cadence just hasthis hardcoded lvs deck name? - LVSDeck(tool_name="pegasus", deck_name="pegasus_lvs", - path=os.path.join(SKY130_CDS, 'Sky130_LVS', 'sky130.lvs.pvl')) + lvs_decks = [ + # LVSDeck(tool_name="calibre", deck_name="calibre_lvs", + # path="$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8"), + # seems like cadence just hasthis hardcoded lvs deck name? + LVSDeck( + tool_name="pegasus", + deck_name="pegasus_lvs", + path=os.path.join(SKY130_CDS, "Sky130_LVS", "sky130.lvs.pvl"), + ) ] else: - raise ValueError( - f"Incorrect standard cell library selection: {slib}") + raise ValueError(f"Incorrect standard cell library selection: {slib}") self.config = TechJSON( name="Skywater 130nm Library", grid_unit="0.001", shrink_factor=None, installs=[ - PathPrefix(id="$SKY130_NDA", - path="technology.sky130.sky130_nda"), + PathPrefix(id="$SKY130_NDA", path="technology.sky130.sky130_nda"), PathPrefix(id="$SKY130A", path="technology.sky130.sky130A"), - PathPrefix(id="$SKY130_CDS", - path="technology.sky130.sky130_cds"), - PathPrefix(id="$SKY130_SCL", - path="technology.sky130.sky130_scl") + PathPrefix(id="$SKY130_CDS", path="technology.sky130.sky130_cds"), + PathPrefix(id="$SKY130_SCL", path="technology.sky130.sky130_scl"), ], libraries=libs, gds_map_file="sky130_lefpin.map", physical_only_cells_list=phys_only, dont_use_list=dont_use, drc_decks=[ - DRCDeck(tool_name="calibre", deck_name="calibre_drc", - path="$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules"), - DRCDeck(tool_name="klayout", deck_name="klayout_drc", - path="$SKY130A/libs.tech/klayout/drc/sky130A.lydrc"), - DRCDeck(tool_name="pegasus", deck_name="pegasus_drc", - path="$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl") + DRCDeck( + tool_name="calibre", + deck_name="calibre_drc", + path="$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules", + ), + DRCDeck( + tool_name="klayout", + deck_name="klayout_drc", + path="$SKY130A/libs.tech/klayout/drc/sky130A.lydrc", + ), + DRCDeck( + tool_name="pegasus", + deck_name="pegasus_drc", + path="$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl", + ), ], additional_drc_text="", lvs_decks=lvs_decks, @@ -406,52 +464,60 @@ def gen_config(self) -> None: sites=sites, stackups=stackups, special_cells=spcl_cells, - extra_prefixes=None + extra_prefixes=None, ) def post_install_script(self) -> None: - self.library_name = 'sky130_fd_sc_hd' + self.library_name = "sky130_fd_sc_hd" # check whether variables were overriden to point to a valid path - self.use_sram22 = os.path.exists(self.get_setting("technology.sky130.sram22_sky130_macros")) + self.use_sram22 = os.path.exists( + self.get_setting("technology.sky130.sram22_sky130_macros") + ) if self.get_setting("technology.sky130.stdcell_library") == "sky130_fd_sc_hd": self.setup_cdl() - #self.setup_verilog() + # self.setup_verilog() self.setup_techlef() - self.logger.info('Loaded Sky130 Tech') - + self.logger.info("Loaded Sky130 Tech") def setup_cdl(self) -> None: - ''' Copy and hack the cdl, replacing pfet_01v8_hvt/nfet_01v8 with - respective names in LVS deck - ''' + """Copy and hack the cdl, replacing pfet_01v8_hvt/nfet_01v8 with + respective names in LVS deck + """ setting_dir = self.get_setting("technology.sky130.sky130A") setting_dir = Path(setting_dir) - source_path = setting_dir / 'libs.ref' / self.library_name / 'cdl' / f'{self.library_name}.cdl' + 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) - dest_path = cache_tech_dir_path / f'{self.library_name}.cdl' + dest_path = cache_tech_dir_path / f"{self.library_name}.cdl" # device names expected in LVS decks - pmos = 'pfet_01v8_hvt' - nmos = 'nfet_01v8' - if (self.get_setting('vlsi.core.lvs_tool') == "hammer.lvs.calibre"): - pmos = 'phighvt' - nmos = 'nshort' - elif (self.get_setting('vlsi.core.lvs_tool') == "hammer.lvs.netgen"): - pmos = 'sky130_fd_pr__pfet_01v8_hvt' - nmos = 'sky130_fd_pr__nfet_01v8' - - 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)) + pmos = "pfet_01v8_hvt" + nmos = "nfet_01v8" + if self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.calibre": + pmos = "phighvt" + nmos = "nshort" + elif self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.netgen": + pmos = "sky130_fd_pr__pfet_01v8_hvt" + nmos = "sky130_fd_pr__nfet_01v8" + + 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) + ) df.write("*.SCALE MICRON\n") for line in sf: - line = line.replace('pfet_01v8_hvt', pmos) - line = line.replace('nfet_01v8', nmos) + line = line.replace("pfet_01v8_hvt", pmos) + line = line.replace("nfet_01v8", nmos) df.write(line) # Copy and hack the verilog @@ -464,123 +530,163 @@ def setup_verilog(self) -> None: setting_dir = Path(setting_dir) # .v - source_path = setting_dir / 'libs.ref' / self.library_name / 'verilog' / f'{self.library_name}.v' + 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) - dest_path = cache_tech_dir_path / f'{self.library_name}.v' + dest_path = cache_tech_dir_path / f"{self.library_name}.v" - 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)) + 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') + 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) # Additionally hack out the specifies sl = [] - with open(dest_path, 'r') as sf: + with open(dest_path, "r") as sf: sl = sf.readlines() # Find timing declaration - start_idx = [idx for idx, line in enumerate(sl) if "`ifndef SKY130_FD_SC_HD__LPFLOW_BLEEDER_1_TIMING_V" in line][0] + start_idx = [ + idx + for idx, line in enumerate(sl) + if "`ifndef SKY130_FD_SC_HD__LPFLOW_BLEEDER_1_TIMING_V" in line + ][0] # Search for the broken statement - search_range = range(start_idx+1, len(sl)) - broken_specify_idx = len(sl)-1 + search_range = range(start_idx + 1, len(sl)) + broken_specify_idx = len(sl) - 1 broken_substr = "(SHORT => VPWR) = (0:0:0,0:0:0,0:0:0,0:0:0,0:0:0,0:0:0);" - broken_specify_idx = [idx for idx in search_range if broken_substr in sl[idx]][0] + broken_specify_idx = [ + idx for idx in search_range if broken_substr in sl[idx] + ][0] endif_idx = [idx for idx in search_range if "`endif" in sl[idx]][0] # Now, delete all the specify statements if specify exists before an endif. if broken_specify_idx < endif_idx: self.logger.info("Removing incorrectly formed specify block.") - cell_def_range = range(start_idx+1, endif_idx) - start_specify_idx = [idx for idx in cell_def_range if "specify" in sl[idx]][0] - end_specify_idx = [idx for idx in cell_def_range if "endspecify" in sl[idx]][0] - sl[start_specify_idx:end_specify_idx+1] = [] # Dice + cell_def_range = range(start_idx + 1, endif_idx) + start_specify_idx = [ + idx for idx in cell_def_range if "specify" in sl[idx] + ][0] + end_specify_idx = [ + idx for idx in cell_def_range if "endspecify" in sl[idx] + ][0] + sl[start_specify_idx : end_specify_idx + 1] = [] # Dice # Deal with the nonexistent net tactfully (don't code in brittle replacements) self.logger.info("Fixing broken net references with select specify blocks.") pattern = r"^\s*wire SLEEP.*B.*delayed;" capture_pattern = r".*(SLEEP.*?B.*?delayed).*" - pattern_idx = [(idx, re.findall(capture_pattern, value)[0]) for idx, value in enumerate(sl) if re.search(pattern, value)] + pattern_idx = [ + (idx, re.findall(capture_pattern, value)[0]) + for idx, value in enumerate(sl) + if re.search(pattern, value) + ] for list_idx, pattern_tuple in enumerate(pattern_idx): - if list_idx != len(pattern_idx)-1: - search_range = range(pattern_tuple[0]+1, pattern_idx[list_idx+1][0]) - else: - search_range = range(pattern_tuple[0]+1, len(sl)) + if list_idx != len(pattern_idx) - 1: + search_range = range(pattern_tuple[0] + 1, pattern_idx[list_idx + 1][0]) + else: + search_range = range(pattern_tuple[0] + 1, len(sl)) for idx in search_range: list = re.findall(capture_pattern, sl[idx]) for elem in list: if elem != pattern_tuple[1]: sl[idx] = sl[idx].replace(elem, pattern_tuple[1]) - self.logger.info(f"Incorrect reference `{elem}` to be replaced with: `{pattern_tuple[1]}` on raw line {idx}.") + self.logger.info( + f"Incorrect reference `{elem}` to be replaced with: `{pattern_tuple[1]}` on raw line {idx}." + ) # Write back into destination - with open(dest_path, 'w') as df: + with open(dest_path, "w") as df: df.writelines(sl) # primitives.v - source_path = setting_dir / 'libs.ref' / \ - self.library_name / 'verilog' / 'primitives.v' + 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) - dest_path = cache_tech_dir_path / 'primitives.v' + dest_path = cache_tech_dir_path / "primitives.v" - 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)) + 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') + 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) - source_path = setting_dir / 'libs.ref' / self.library_name / 'techlef' / f'{self.library_name}__nom.tlef' + 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) - dest_path = cache_tech_dir_path / f'{self.library_name}__nom.tlef' + dest_path = cache_tech_dir_path / f"{self.library_name}__nom.tlef" - 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)) + 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': + if line.strip() == "END pwell": df.write(_the_tlef_edit) - - - # syn_power seems to not take hooks from `get_tech_syn_hooks` + # syn_power seems to not take hooks from `get_tech_syn_hooks` # also, syn_power is called from joules so we need to add the hook to joules # TODO: clean this up, there should be a way to always include this hook whenever genus will be invoked def get_tech_power_hooks(self, tool_name: str) -> List[HammerToolHookAction]: hooks = {} + def enable_scl_clk_gating_cell_hook(ht: HammerTool) -> bool: - ht.append("set_db [get_db lib_cells -if {.base_name == ICGX1}] .avoid false") + ht.append( + "set_db [get_db lib_cells -if {.base_name == ICGX1}] .avoid false" + ) return True - # The clock gating cell is set to don't touch/use in the cadence pdk (as of v0.0.3), work around that - if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": - hooks["joules"] = [HammerTool.make_pre_insertion_hook("synthesize_design", enable_scl_clk_gating_cell_hook)] + if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": + hooks["joules"] = [ + HammerTool.make_pre_insertion_hook( + "synthesize_design", enable_scl_clk_gating_cell_hook + ) + ] return hooks.get(tool_name, []) @@ -588,28 +694,49 @@ def get_tech_syn_hooks(self, tool_name: str) -> List[HammerToolHookAction]: hooks = {} def enable_scl_clk_gating_cell_hook(ht: HammerTool) -> bool: - ht.append("set_db [get_db lib_cells -if {.base_name == ICGX1}] .avoid false") + ht.append( + "set_db [get_db lib_cells -if {.base_name == ICGX1}] .avoid false" + ) return True # The clock gating cell is set to don't touch/use in the cadence pdk (as of v0.0.3), work around that - if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": - hooks["genus"] = [HammerTool.make_pre_insertion_hook("syn_generic", enable_scl_clk_gating_cell_hook)] + if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": + hooks["genus"] = [ + HammerTool.make_pre_insertion_hook( + "syn_generic", enable_scl_clk_gating_cell_hook + ) + ] + # seems to mess up lvs for now + # hooks['genus'].append(HammerTool.make_removal_hook("add_tieoffs")) return hooks.get(tool_name, []) def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]: hooks = { "innovus": [ - HammerTool.make_post_insertion_hook( "init_design", sky130_innovus_settings), - HammerTool.make_pre_insertion_hook( "power_straps", sky130_connect_nets), - HammerTool.make_pre_insertion_hook( "write_design", sky130_connect_nets2) - ]} + HammerTool.make_post_insertion_hook( + "init_design", sky130_innovus_settings + ), + HammerTool.make_pre_insertion_hook("power_straps", sky130_connect_nets), + HammerTool.make_pre_insertion_hook( + "write_design", sky130_connect_nets2 + ), + ] + } # there are no cap/decap cells in the cadence stdcell library as of version 0.0.3, so we can't do things that reference them - if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": - hooks['innovus'].append(HammerTool.make_replacement_hook("power_straps", power_straps_no_tapcells)) - else: - hooks['innovus'].append(HammerTool.make_pre_insertion_hook( "place_tap_cells", sky130_add_endcaps)) + if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": + hooks["innovus"].append( + HammerTool.make_replacement_hook( + "power_straps", power_rail_straps_no_tapcells + ) + ) + else: + hooks["innovus"].append( + HammerTool.make_pre_insertion_hook( + "place_tap_cells", sky130_add_endcaps + ) + ) return hooks.get(tool_name, []) @@ -617,63 +744,96 @@ def get_tech_drc_hooks(self, tool_name: str) -> List[HammerToolHookAction]: calibre_hooks = [] pegasus_hooks = [] if self.get_setting("technology.sky130.drc_blackbox_srams"): - calibre_hooks.append(HammerTool.make_post_insertion_hook("generate_drc_run_file", calibre_drc_blackbox_srams)) - pegasus_hooks.append(HammerTool.make_post_insertion_hook("generate_drc_ctl_file", pegasus_drc_blackbox_srams)) - hooks = {"calibre": calibre_hooks, - "pegasus": pegasus_hooks - } + calibre_hooks.append( + HammerTool.make_post_insertion_hook( + "generate_drc_run_file", calibre_drc_blackbox_srams + ) + ) + pegasus_hooks.append( + HammerTool.make_post_insertion_hook( + "generate_drc_ctl_file", pegasus_drc_blackbox_srams + ) + ) + hooks = {"calibre": calibre_hooks, "pegasus": pegasus_hooks} return hooks.get(tool_name, []) def get_tech_lvs_hooks(self, tool_name: str) -> List[HammerToolHookAction]: - calibre_hooks = [HammerTool.make_post_insertion_hook("generate_lvs_run_file", setup_calibre_lvs_deck)] + calibre_hooks = [ + HammerTool.make_post_insertion_hook( + "generate_lvs_run_file", setup_calibre_lvs_deck + ) + ] pegasus_hooks = [] if self.use_sram22: - calibre_hooks.append(HammerTool.make_post_insertion_hook("generate_lvs_run_file", sram22_lvs_recognize_gates_all)) + calibre_hooks.append( + HammerTool.make_post_insertion_hook( + "generate_lvs_run_file", sram22_lvs_recognize_gates_all + ) + ) if self.get_setting("technology.sky130.lvs_blackbox_srams"): - calibre_hooks.append(HammerTool.make_post_insertion_hook("generate_lvs_run_file", calibre_lvs_blackbox_srams)) + calibre_hooks.append( + HammerTool.make_post_insertion_hook( + "generate_lvs_run_file", calibre_lvs_blackbox_srams + ) + ) if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": - pegasus_hooks.append(HammerTool.make_post_insertion_hook("generate_lvs_ctl_file", pegasus_lvs_add_130a_primitives)) - hooks = {"calibre": calibre_hooks, - "pegasus": pegasus_hooks - } + pegasus_hooks.append( + HammerTool.make_post_insertion_hook( + "generate_lvs_ctl_file", pegasus_lvs_add_130a_primitives + ) + ) + hooks = {"calibre": calibre_hooks, "pegasus": pegasus_hooks} return hooks.get(tool_name, []) @staticmethod def openram_sram_names() -> List[str]: - """ Return a list of cell-names of the OpenRAM SRAMs (that we'll use). """ + """Return a list of cell-names of the OpenRAM SRAMs (that we'll use).""" return [ "sky130_sram_1kbyte_1rw1r_32x256_8", "sky130_sram_1kbyte_1rw1r_8x1024_8", - "sky130_sram_2kbyte_1rw1r_32x512_8" + "sky130_sram_2kbyte_1rw1r_32x512_8", ] @staticmethod def sky130_sram_primitive_names() -> List[str]: - # TODO: fix this - spice_filenames = ["sky130_fd_pr__pfet_01v8", "sky130_fd_pr__nfet_01v8", "sky130_fd_pr__pfet_01v8_hvt", "sky130_fd_pr__special_nfet_latch", "sky130_fd_pr__special_nfet_pass"] #"sky130_fd_pr__special_pfet_latch", ] + spice_filenames = [ + "sky130_fd_pr__pfet_01v8", + "sky130_fd_pr__nfet_01v8", + "sky130_fd_pr__pfet_01v8_hvt", + "sky130_fd_pr__special_nfet_latch", + "sky130_fd_pr__special_nfet_pass", + ] # "sky130_fd_pr__special_pfet_latch", ] paths = [] for fname in spice_filenames: - paths.append(f"/tools/commercial/skywater/local/sky130A/libs.ref/sky130_fd_pr/spice/{fname}.pm3.spice") + paths.append( + f"/tools/commercial/skywater/local/sky130A/libs.ref/sky130_fd_pr/spice/{fname}.pm3.spice" + ) # TODO: this is bc line 535 in the bwrc one causes a syntax error - paths.append("/tools/C/elamdf/chipyard_dev/vlsi/sky130_fd_pr__special_pfet_latch.pm3.spice") + paths.append( + "/tools/C/elamdf/chipyard_dev/vlsi/sky130_fd_pr__special_pfet_latch.pm3.spice" + ) return paths @staticmethod def sky130_sram_names() -> List[str]: sky130_sram_names = [] - sram_cache_json = importlib.resources.files("hammer.technology.sky130").joinpath("sram-cache.json").read_text() + sram_cache_json = ( + importlib.resources.files("hammer.technology.sky130") + .joinpath("sram-cache.json") + .read_text() + ) dl = json.loads(sram_cache_json) for d in dl: - sky130_sram_names.append(d['name']) + sky130_sram_names.append(d["name"]) return sky130_sram_names -_the_tlef_edit = ''' +_the_tlef_edit = """ LAYER licon TYPE CUT ; END licon -''' +""" # various Innovus database settings @@ -682,7 +842,7 @@ def sky130_innovus_settings(ht: HammerTool) -> bool: assert isinstance(ht, TCLTool), "innovus settings can only run on TCL tools" """Settings for every tool invocation""" ht.append( - ''' + """ ########################################################## # Placement attributes [get_db -category place] @@ -732,30 +892,37 @@ def sky130_innovus_settings(ht: HammerTool) -> bool: set_db route_design_concurrent_minimize_via_count_effort high set_db opt_consider_routing_congestion true set_db route_design_detail_use_multi_cut_via_effort medium - ''' + """ ) if ht.hierarchical_mode in {HierarchicalMode.Top, HierarchicalMode.Flat}: ht.append( - ''' + """ # For top module: snap die to manufacturing grid, not placement grid set_db floorplan_snap_die_grid manufacturing - ''' + """ ) 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 -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)) + 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 -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 @@ -767,18 +934,19 @@ def sky130_connect_nets2(ht: HammerTool) -> bool: def sky130_add_endcaps(ht: HammerTool) -> bool: assert isinstance(ht, HammerPlaceAndRouteTool), "endcap insertion only for par" assert isinstance(ht, TCLTool), "endcap insertion can only run on TCL tools" - endcap_cells=ht.technology.get_special_cell_by_type(CellType.EndCap) - endcap_cell=endcap_cells[0].name[0] + endcap_cells = ht.technology.get_special_cell_by_type(CellType.EndCap) + endcap_cell = endcap_cells[0].name[0] ht.append( - f''' + f""" set_db add_endcaps_boundary_tap true set_db add_endcaps_left_edge {endcap_cell} set_db add_endcaps_right_edge {endcap_cell} add_endcaps - ''' + """ ) return True + def efabless_ring_io(ht: HammerTool) -> bool: assert isinstance(ht, HammerPlaceAndRouteTool), "IO ring instantiation only for par" assert isinstance(ht, TCLTool), "IO ring instantiation can only run on TCL tools" @@ -786,7 +954,8 @@ def efabless_ring_io(ht: HammerTool) -> bool: ht.append(f"read_io_file {io_file} -no_die_size_adjust") p_nets = list(map(lambda s: s.name, ht.get_independent_power_nets())) g_nets = list(map(lambda s: s.name, ht.get_independent_ground_nets())) - ht.append(f''' + ht.append( + f""" # Global net connections connect_global_net VDDA -type pg_pin -pin_base_name VDDA -verbose connect_global_net VDDIO -type pg_pin -pin_base_name VDDIO* -verbose @@ -796,8 +965,10 @@ def efabless_ring_io(ht: HammerTool) -> bool: connect_global_net {g_nets[0]} -type pg_pin -pin_base_name VSSA -verbose connect_global_net {g_nets[0]} -type pg_pin -pin_base_name VSSIO* -verbose connect_global_net {g_nets[0]} -type pg_pin -pin_base_name VSSD* -verbose - ''') - ht.append(''' + """ + ) + ht.append( + """ # IO fillers set io_fillers {sky130_ef_io__connect_vcchib_vccd_and_vswitch_vddio_slice_20um sky130_ef_io__com_bus_slice_10um sky130_ef_io__com_bus_slice_5um sky130_ef_io__com_bus_slice_1um} add_io_fillers -prefix IO_FILLER -io_ring 1 -cells $io_fillers -side top -filler_orient r0 @@ -807,23 +978,33 @@ def efabless_ring_io(ht: HammerTool) -> bool: # Fix placement set io_filler_insts [get_db insts IO_FILLER_*] set_db $io_filler_insts .place_status fixed - ''') + """ + ) # An offset of 40um is used to place the core ring inside the core area. It # can be decreased down to 5um as desired, but will require additional # routing / settings to connect the core power stripes to the ring. - ht.append(f''' + ht.append( + f""" # Core ring add_rings -follow io -layer met5 -nets {{ {p_nets[0]} {g_nets[0]} }} -offset 40 -width 13 -spacing 3 route_special -connect pad_pin -nets {{ {p_nets[0]} {g_nets[0]} }} -detailed_log - ''') - ht.append(''' + """ + ) + ht.append( + """ # Prevent buffering on TIE_LO_ESD and TIE_HI_ESD set_dont_touch [get_db [get_db pins -if {.name == *TIE*ESD}] .net] - ''') + """ + ) return True -def power_straps_no_tapcells(ht: HammerTool) -> bool: - ht.append(''' + +def power_rail_straps_no_tapcells(ht: HammerTool) -> bool: + # We do this since there are no explicit tapcells in sky130_scl + + # just need the rail ones, others are placed as usual. + ht.append( + """ # -------------------------------------------------------------------------------- # This script was written and developed by HAMMER at UC Berkeley; however, the # underlying commands and reports are copyrighted by Cadence. We thank Cadence for @@ -833,19 +1014,42 @@ def power_straps_no_tapcells(ht: HammerTool) -> bool: # Power strap definition for layer met1 (rails): -set_db add_stripes_stacked_via_bottom_layer met1 +# should be .14 set_db add_stripes_stacked_via_top_layer met1 +set_db add_stripes_stacked_via_bottom_layer met1 +set_db add_stripes_spacing_from_block 4.000 +#add_stripes -over_physical_pins 1 -nets {VDD VSS} -width .14 -direction horizontal -pin_layer met1 -layer met1 +#add_stripes -layer met1 -over_pins 1 -number_of_sets 1 -spacing 3.74 -direction horizontal -width .4 -nets { VSS VDD } -number_of_sets 1 +#add_stripes -pin_layer met1 -layer met1 -over_pins 1 -spacing .2 -direction horizontal -width .4 -nets { VSS VDD } +#add_stripes -master "FILL*" -over_pins 1 -block_ring_bottom_layer_limit met1 -block_ring_top_layer_limit met1 -direction horizontal -layer met1 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met1 -width pin_width +#add_stripes -nets {VDD VSS} -layer met1 -direction horizontal -width .4 -spacing 4.54 -set_to_set_distance 9.08 -start_from bottom -pin_offset -2.46 +#add_stripes -nets {VDD VSS} -layer met1 -direction horizontal -width .4 -spacing 3.74 -number_of_sets 1 -start_from left -switch_layer_over_obs false -max_same_layer_jog_length 2 -pad_core_ring_top_layer_limit met5 -pad_core_ring_bottom_layer_limit met1 -block_ring_top_layer_limit met5 -block_ring_bottom_layer_limit met1 -use_wire_group 0 -snap_wire_center_to_grid none +add_stripes -nets {VDD VSS} -layer met1 -direction horizontal -start_offset -.2 -width .4 -spacing 3.74 -set_to_set_distance 8.28 -start_from bottom -switch_layer_over_obs false -max_same_layer_jog_length 2 -pad_core_ring_top_layer_limit met5 -pad_core_ring_bottom_layer_limit met1 -block_ring_top_layer_limit met5 -block_ring_bottom_layer_limit met1 -use_wire_group 0 -snap_wire_center_to_grid none + +# Power strap definition for layer met2: + +set_db add_stripes_stacked_via_top_layer met2 +set_db add_stripes_stacked_via_bottom_layer met1 +set_db add_stripes_trim_antenna_back_to_shape {stripe} +#set_db add_stripes_spacing_from_block 4.000 +#add_stripes -create_pins 0 -block_ring_bottom_layer_limit met2 -block_ring_top_layer_limit met1 -direction vertical -layer met2 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met1 -set_to_set_distance 101.20 -spacing 2.26 -switch_layer_over_obs 0 -width 1.42 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 0] + 4.81] +add_stripes -nets {VDD VSS} -layer met2 -direction vertical -width .2 -spacing 0.14 -number_of_sets 1 -extend_to all_domains -start_from left -switch_layer_over_obs false -max_same_layer_jog_length 2 -pad_core_ring_top_layer_limit met5 -pad_core_ring_bottom_layer_limit met1 -block_ring_top_layer_limit met5 -block_ring_bottom_layer_limit met1 -use_wire_group 0 -snap_wire_center_to_grid none + +# Power strap definition for layer met3: + +set_db add_stripes_stacked_via_top_layer met3 +set_db add_stripes_stacked_via_bottom_layer met2 +set_db add_stripes_trim_antenna_back_to_shape {stripe} set_db add_stripes_spacing_from_block 2.000 -# Provide some stdcells to use as reference because the Cadence (v0.0.3) pdk doesn't have tapcells -add_stripes -pin_layer met1 -layer met1 -over_pins 1 -master SDFF* -block_ring_bottom_layer_limit met1 -block_ring_top_layer_limit met1 -pad_core_ring_bottom_layer_limit met1 -pad_core_ring_top_layer_limit met1 -direction horizontal -width pin_width -nets { VSS VDD } +add_stripes -create_pins 0 -block_ring_bottom_layer_limit met3 -block_ring_top_layer_limit met2 -direction horizontal -layer met3 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met2 -set_to_set_distance 75.90 -spacing 3.66 -switch_layer_over_obs 0 -width 1.86 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 1] + 7.35] # Power strap definition for layer met4: set_db add_stripes_stacked_via_top_layer met4 -set_db add_stripes_stacked_via_bottom_layer met1 +set_db add_stripes_stacked_via_bottom_layer met3 set_db add_stripes_trim_antenna_back_to_shape {stripe} set_db add_stripes_spacing_from_block 2.000 -add_stripes -create_pins 0 -block_ring_bottom_layer_limit met4 -block_ring_top_layer_limit met1 -direction vertical -layer met4 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met1 -set_to_set_distance 75.90 -spacing 3.66 -switch_layer_over_obs 0 -width 1.86 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 0] + 7.35] +add_stripes -create_pins 0 -block_ring_bottom_layer_limit met4 -block_ring_top_layer_limit met3 -direction vertical -layer met4 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met3 -set_to_set_distance 75.90 -spacing 3.66 -switch_layer_over_obs 0 -width 1.86 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 0] + 7.35] # Power strap definition for layer met5: @@ -854,13 +1058,15 @@ def power_straps_no_tapcells(ht: HammerTool) -> bool: set_db add_stripes_trim_antenna_back_to_shape {stripe} set_db add_stripes_spacing_from_block 2.000 add_stripes -create_pins 1 -block_ring_bottom_layer_limit met5 -block_ring_top_layer_limit met4 -direction horizontal -layer met5 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met4 -set_to_set_distance 225.40 -spacing 17.68 -switch_layer_over_obs 0 -width 1.64 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 1] + 5.62] -''') + +""" + ) return True def calibre_drc_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerDRCTool), "Exlude SRAMs only in DRC" - drc_box = '' + drc_box = "" for name in SKY130Tech.sky130_sram_names(): drc_box += f"\nEXCLUDE CELL {name}" run_file = ht.drc_run_file # type: ignore @@ -868,9 +1074,10 @@ def calibre_drc_blackbox_srams(ht: HammerTool) -> bool: f.write(drc_box) return True + def pegasus_drc_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerDRCTool), "Exlude SRAMs only in DRC" - drc_box = '' + drc_box = "" for name in SKY130Tech.sky130_sram_names(): drc_box += f"\nexclude_cell {name}" run_file = ht.drc_ctl_file # type: ignore @@ -878,9 +1085,10 @@ def pegasus_drc_blackbox_srams(ht: HammerTool) -> bool: f.write(drc_box) return True + def calibre_lvs_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" - lvs_box = '' + lvs_box = "" for name in SKY130Tech.sky130_sram_names(): lvs_box += f"\nLVS BOX {name}" lvs_box += f"\nLVS FILTER {name} OPEN " @@ -889,21 +1097,25 @@ def calibre_lvs_blackbox_srams(ht: HammerTool) -> bool: f.write(lvs_box) return True + # required for sram22 since they use the 130a primiviites def pegasus_lvs_add_130a_primitives(ht: HammerTool) -> bool: + return True assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" - lvs_box = '' + lvs_box = "" for name in SKY130Tech.sky130_sram_primitive_names(): lvs_box += f"""\nschematic_path "{name}" spice;""" # this is because otherwise lvs crashes with tons of stdcell-level pin mismatches # TODO elam move this somewhere better - #lvs_box += f"""\nlvs_discard_pins yes;""" - #lvs_box += f"""\nlvs_expand_cell_on_error yes;""" + # lvs_box += f"""\nlvs_discard_pins yes;""" + # lvs_box += f"""\nlvs_expand_cell_on_error yes;""" lvs_box += f"""\nlvs_inconsistent_reduction_threshold -none;""" run_file = ht.lvs_ctl_file # type: ignore with open(run_file, "r+") as f: # Remove SRAM SPICE file includes. - pattern = 'schematic_path.*({}).*spice;\n'.format('|'.join(SKY130Tech.sky130_sram_primitive_names())) + pattern = "schematic_path.*({}).*spice;\n".format( + "|".join(SKY130Tech.sky130_sram_primitive_names()) + ) matcher = re.compile(pattern) contents = f.read() fixed_contents = contents + lvs_box @@ -911,15 +1123,20 @@ def pegasus_lvs_add_130a_primitives(ht: HammerTool) -> bool: f.write(fixed_contents) return True + def pegasus_lvs_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" - lvs_box = '' - for name in SKY130Tech.sky130_sram_names() + SKY130Tech.sky130_sram_primitive_names(): + lvs_box = "" + for name in ( + SKY130Tech.sky130_sram_names() + SKY130Tech.sky130_sram_primitive_names() + ): lvs_box += f"\nlvs_black_box {name} -gray" run_file = ht.lvs_ctl_file # type: ignore with open(run_file, "r+") as f: # Remove SRAM SPICE file includes. - pattern = 'schematic_path.*({}).*spice;\n'.format('|'.join(SKY130Tech.sky130_sram_names())) + pattern = "schematic_path.*({}).*spice;\n".format( + "|".join(SKY130Tech.sky130_sram_names()) + ) matcher = re.compile(pattern) contents = f.read() fixed_contents = matcher.sub("", contents) + lvs_box @@ -927,8 +1144,11 @@ def pegasus_lvs_blackbox_srams(ht: HammerTool) -> bool: f.write(fixed_contents) return True + def sram22_lvs_recognize_gates_all(ht: HammerTool) -> bool: - assert isinstance(ht, HammerLVSTool), "Change 'LVS RECOGNIZE GATES' from 'NONE' to 'ALL' for SRAM22" + assert isinstance( + ht, HammerLVSTool + ), "Change 'LVS RECOGNIZE GATES' from 'NONE' to 'ALL' for SRAM22" run_file = ht.lvs_run_file # type: ignore with open(run_file, "a") as f: f.write("LVS RECOGNIZE GATES ALL") @@ -941,42 +1161,42 @@ def sram22_lvs_recognize_gates_all(ht: HammerTool) -> bool: "SOURCE SYSTEM SPICE", "SOURCE PATH", "ERC", - "LVS REPORT" + "LVS REPORT", ] -LVS_DECK_INSERT_LINES = ''' +LVS_DECK_INSERT_LINES = """ LVS FILTER D OPEN SOURCE LVS FILTER D OPEN LAYOUT -''' +""" def setup_calibre_lvs_deck(ht: HammerTool) -> bool: - assert isinstance( - ht, HammerLVSTool), "Modify Calibre LVS deck for LVS only" + assert isinstance(ht, HammerLVSTool), "Modify Calibre LVS deck for LVS only" # Remove conflicting specification statements found in PDK LVS decks - pattern = '.*({}).*\n'.format('|'.join(LVS_DECK_SCRUB_LINES)) + pattern = ".*({}).*\n".format("|".join(LVS_DECK_SCRUB_LINES)) matcher = re.compile(pattern) - source_paths = ht.get_setting('technology.sky130.lvs_deck_sources') + source_paths = ht.get_setting("technology.sky130.lvs_deck_sources") lvs_decks = ht.technology.config.lvs_decks if not lvs_decks: return True - for i,deck in enumerate(lvs_decks): - if deck.tool_name != 'calibre': continue + for i, deck in enumerate(lvs_decks): + if deck.tool_name != "calibre": + continue try: source_path = Path(source_paths[i]) except IndexError: - ht.logger.error( - 'No corresponding source for LVS deck {}'.format(deck)) + ht.logger.error("No corresponding source for LVS deck {}".format(deck)) continue if not source_path.exists(): raise FileNotFoundError(f"LVS deck not found: {source_path}") dest_path = deck.path ht.technology.ensure_dirs_exist(dest_path) - with open(source_path, 'r') as sf: - with open(dest_path, 'w') as df: - ht.logger.info("Modifying LVS deck: {} -> {}".format - (source_path, dest_path)) + with open(source_path, "r") as sf: + with open(dest_path, "w") as df: + ht.logger.info( + "Modifying LVS deck: {} -> {}".format(source_path, dest_path) + ) df.write(matcher.sub("", sf.read())) df.write(LVS_DECK_INSERT_LINES) return True From f1c67c3699af7c663f9f6861a8a57f06d8b87081 Mon Sep 17 00:00:00 2001 From: elamdf Date: Tue, 27 Aug 2024 03:19:23 -0700 Subject: [PATCH 26/68] seperate drc decks to be library specific, set PEGASUS_DRC to make pegasus work. drc has like 15 random enclousure violations.. not sure why, they don't seem to be systemic? --- hammer/drc/pegasus/__init__.py | 53 ++++++++++++++++++++-------- hammer/technology/sky130/__init__.py | 46 +++++++++++++++--------- 2 files changed, 67 insertions(+), 32 deletions(-) diff --git a/hammer/drc/pegasus/__init__.py b/hammer/drc/pegasus/__init__.py index 1f5b3509e..3d899e3c8 100644 --- a/hammer/drc/pegasus/__init__.py +++ b/hammer/drc/pegasus/__init__.py @@ -30,7 +30,9 @@ def empty_step(self) -> bool: @property def steps(self) -> List[HammerToolStep]: - steps = [self.generate_drc_ctl_file] # TODO: DRC steps require multiple runs of the tool how do we support this? + steps = [ + self.generate_drc_ctl_file + ] # TODO: DRC steps require multiple runs of the tool how do we support this? return self.make_steps_from_methods(steps) def do_post_steps(self) -> bool: @@ -50,17 +52,22 @@ def run_pegasus(self) -> bool: args = [ pegasus_bin, "-drc", # DRC mode - "-dp", str(self.get_setting("vlsi.core.max_threads")), + "-dp", + str(self.get_setting("vlsi.core.max_threads")), "-license_dp_continue", # don't quit if requested dp license not available - "-control", self.drc_ctl_file, - "-log_dir", f"{self.top_module}_logs", - "-ui_data" # for results viewer + "-control", + self.drc_ctl_file, + "-log_dir", + f"{self.top_module}_logs", + "-ui_data", # for results viewer # TODO: -interactive for block level - ] + rules + ] + rules HammerVLSILogging.enable_colour = False HammerVLSILogging.enable_tag = False - self.run_executable(args, cwd=self.run_dir) # TODO: check for errors and deal with them + self.run_executable( + args, cwd=self.run_dir + ) # TODO: check for errors and deal with them HammerVLSILogging.enable_colour = True HammerVLSILogging.enable_tag = True @@ -72,31 +79,43 @@ def run_pegasus(self) -> bool: technology = self.get_setting("vlsi.core.technology").split(".")[-1] with open(self.view_drc_script, "w") as f: - f.write(textwrap.dedent(f""" + f.write( + textwrap.dedent( + f""" cd {self.run_dir} source ./enter {pegasus_bin}DesignReview -qrv -tech {technology} -data {self.layout_file} -post {self.dr_rv_macro} -verbose - """)) + """ + ) + ) os.chmod(self.view_drc_script, 0o755) with open(self.dr_rv_macro, "w") as f: - f.write(textwrap.dedent(f''' + f.write( + textwrap.dedent( + f""" PVS::invoke_pvsrv("{self.run_dir}"); - ''')) + """ + ) + ) return True def generate_drc_ctl_file(self) -> bool: - """ Generate the DRC control file self.drc_ctl_file and fill its contents """ + """Generate the DRC control file self.drc_ctl_file and fill its contents""" with open(self.drc_ctl_file, "w") as f: - f.write(self.header.replace("#","//")) - f.write(textwrap.dedent(f''' + f.write(self.header.replace("#", "//")) + f.write( + textwrap.dedent( + f""" virtual_connect -report yes; layout_path "{self.layout_file}"; layout_primary {self.top_module}; results_db -drc "{self.drc_results_db}" -ascii; report_summary -drc "{self.drc_results_file}" -replace; - ''')) + """ + ) + ) f.write(self.get_additional_drc_text()) return True @@ -132,10 +151,14 @@ def env_vars(self) -> Dict[str, str]: """ v = dict(super().env_vars) v["PEGASUS_BIN"] = self.get_setting("drc.pegasus.pegasus_bin") + v["PEGASUS_DRC"] = os.path.join( + self.get_setting("technology.sky130.sky130_cds"), "Sky130_DRC" + ) return v @property def post_synth_sdc(self) -> Optional[str]: pass + tool = PegasusDRC diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 82988a26f..c60a92f2c 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -314,6 +314,25 @@ def gen_config(self) -> None: path="$SKY130_CDS/Sky130_LVS/sky130.lvs.pvl", ), ] + drc_decks = ( + [ + DRCDeck( + tool_name="calibre", + deck_name="calibre_drc", + path="$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules", + ), + DRCDeck( + tool_name="klayout", + deck_name="klayout_drc", + path="$SKY130A/libs.tech/klayout/drc/sky130A.lydrc", + ), + DRCDeck( + tool_name="pegasus", + deck_name="pegasus_drc", + path="$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl", + ), + ], + ) elif slib == "sky130_scl": @@ -422,6 +441,15 @@ def gen_config(self) -> None: path=os.path.join(SKY130_CDS, "Sky130_LVS", "sky130.lvs.pvl"), ) ] + drc_decks = [ + DRCDeck( + tool_name="pegasus", + deck_name="pegasus_drc", + path=os.path.join( + SKY130_CDS, "Sky130_DRC", "sky130_rev_0.0_1.0.drc.pvl" + ), + ) + ] else: raise ValueError(f"Incorrect standard cell library selection: {slib}") @@ -440,25 +468,9 @@ def gen_config(self) -> None: gds_map_file="sky130_lefpin.map", physical_only_cells_list=phys_only, dont_use_list=dont_use, - drc_decks=[ - DRCDeck( - tool_name="calibre", - deck_name="calibre_drc", - path="$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules", - ), - DRCDeck( - tool_name="klayout", - deck_name="klayout_drc", - path="$SKY130A/libs.tech/klayout/drc/sky130A.lydrc", - ), - DRCDeck( - tool_name="pegasus", - deck_name="pegasus_drc", - path="$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl", - ), - ], additional_drc_text="", lvs_decks=lvs_decks, + drc_decks=drc_decks, additional_lvs_text="", tarballs=None, sites=sites, From 8500f3be568ebfcc7ca77edc64b86448fe7ca9a4 Mon Sep 17 00:00:00 2001 From: elamdf Date: Sat, 19 Oct 2024 20:14:03 -0700 Subject: [PATCH 27/68] don't draw straps on met2 or met3 to prevent congestion --- hammer/technology/sky130/__init__.py | 29 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index c60a92f2c..fa8f7a498 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -1040,28 +1040,29 @@ def power_rail_straps_no_tapcells(ht: HammerTool) -> bool: # Power strap definition for layer met2: -set_db add_stripes_stacked_via_top_layer met2 -set_db add_stripes_stacked_via_bottom_layer met1 -set_db add_stripes_trim_antenna_back_to_shape {stripe} -#set_db add_stripes_spacing_from_block 4.000 -#add_stripes -create_pins 0 -block_ring_bottom_layer_limit met2 -block_ring_top_layer_limit met1 -direction vertical -layer met2 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met1 -set_to_set_distance 101.20 -spacing 2.26 -switch_layer_over_obs 0 -width 1.42 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 0] + 4.81] -add_stripes -nets {VDD VSS} -layer met2 -direction vertical -width .2 -spacing 0.14 -number_of_sets 1 -extend_to all_domains -start_from left -switch_layer_over_obs false -max_same_layer_jog_length 2 -pad_core_ring_top_layer_limit met5 -pad_core_ring_bottom_layer_limit met1 -block_ring_top_layer_limit met5 -block_ring_bottom_layer_limit met1 -use_wire_group 0 -snap_wire_center_to_grid none +#set_db add_stripes_stacked_via_top_layer met2 +#set_db add_stripes_stacked_via_bottom_layer met1 +#set_db add_stripes_trim_antenna_back_to_shape {stripe} +##set_db add_stripes_spacing_from_block 4.000 +##add_stripes -create_pins 0 -block_ring_bottom_layer_limit met2 -block_ring_top_layer_limit met1 -direction vertical -layer met2 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met1 -set_to_set_distance 101.20 -spacing 2.26 -switch_layer_over_obs 0 -width 1.42 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 0] + 4.81] +#add_stripes -nets {VDD VSS} -layer met2 -direction vertical -width .2 -spacing 0.14 -number_of_sets 1 -extend_to all_domains -start_from left -switch_layer_over_obs false -max_same_layer_jog_length 2 -pad_core_ring_top_layer_limit met5 -pad_core_ring_bottom_layer_limit met1 -block_ring_top_layer_limit met5 -block_ring_bottom_layer_limit met1 -use_wire_group 0 -snap_wire_center_to_grid none -# Power strap definition for layer met3: +## Power strap definition for layer met3: -set_db add_stripes_stacked_via_top_layer met3 -set_db add_stripes_stacked_via_bottom_layer met2 -set_db add_stripes_trim_antenna_back_to_shape {stripe} -set_db add_stripes_spacing_from_block 2.000 -add_stripes -create_pins 0 -block_ring_bottom_layer_limit met3 -block_ring_top_layer_limit met2 -direction horizontal -layer met3 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met2 -set_to_set_distance 75.90 -spacing 3.66 -switch_layer_over_obs 0 -width 1.86 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 1] + 7.35] +#set_db add_stripes_stacked_via_top_layer met3 +#set_db add_stripes_stacked_via_bottom_layer met2 +#set_db add_stripes_trim_antenna_back_to_shape {stripe} +#set_db add_stripes_spacing_from_block 2.000 +#add_stripes -create_pins 0 -block_ring_bottom_layer_limit met3 -block_ring_top_layer_limit met2 -direction horizontal -layer met3 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met2 -set_to_set_distance 75.90 -spacing 3.66 -switch_layer_over_obs 0 -width 1.86 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 1] + 7.35] # Power strap definition for layer met4: set_db add_stripes_stacked_via_top_layer met4 -set_db add_stripes_stacked_via_bottom_layer met3 +set_db add_stripes_stacked_via_bottom_layer met1 set_db add_stripes_trim_antenna_back_to_shape {stripe} set_db add_stripes_spacing_from_block 2.000 -add_stripes -create_pins 0 -block_ring_bottom_layer_limit met4 -block_ring_top_layer_limit met3 -direction vertical -layer met4 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met3 -set_to_set_distance 75.90 -spacing 3.66 -switch_layer_over_obs 0 -width 1.86 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 0] + 7.35] +#add_stripes -create_pins 0 -block_ring_bottom_layer_limit met4 -block_ring_top_layer_limit met3 -direction vertical -layer met4 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met3 -set_to_set_distance 75.90 -spacing 3.66 -switch_layer_over_obs 0 -width 1.86 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 0] + 7.35] +add_stripes -create_pins 0 -block_ring_bottom_layer_limit met4 -block_ring_top_layer_limit met1 -direction vertical -layer met4 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met1 -set_to_set_distance 75.90 -spacing 3.66 -switch_layer_over_obs 0 -width 1.86 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 0] + 7.35] # Power strap definition for layer met5: From a9e4780741df4dff4c51b6c80d26c232c475d605 Mon Sep 17 00:00:00 2001 From: elamdf Date: Mon, 21 Oct 2024 10:10:09 -0700 Subject: [PATCH 28/68] remove sram with bad lef from cache --- hammer/technology/sky130/sram-cache.json | 28 ------------------------ 1 file changed, 28 deletions(-) diff --git a/hammer/technology/sky130/sram-cache.json b/hammer/technology/sky130/sram-cache.json index 045da80ef..3683de4ed 100644 --- a/hammer/technology/sky130/sram-cache.json +++ b/hammer/technology/sky130/sram-cache.json @@ -195,34 +195,6 @@ } ] }, - { - "type": "sram", - "name": "sram22_2048x32m8w8", - "source": "sram22", - "depth": "2048", - "width": 32, - "family": "1rw", - "mask": "true", - "vt": "svt", - "mux": 8, - "ports": [ - { - "address port name": "addr", - "address port polarity": "active high", - "clock port name": "clk", - "clock port polarity": "active high", - "write enable port name": "we", - "write enable port polarity": "active high", - "output port name": "dout", - "output port polarity": "active high", - "input port name": "din", - "input port polarity": "active high", - "mask port name": "wmask", - "mask granularity": 8, - "mask port polarity": "active high" - } - ] - }, { "type": "sram", "name": "sram22_1024x32m8w32", From d19a3dbf53fc9df5497937db152aeedff599eb1d Mon Sep 17 00:00:00 2001 From: elamdf Date: Wed, 23 Oct 2024 20:14:35 -0700 Subject: [PATCH 29/68] hack tlefs for both cadence and 130a stdcells, since we need to use the 130a IO cells anyways --- hammer/technology/sky130/__init__.py | 61 ++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index fa8f7a498..29395ddc3 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -58,7 +58,8 @@ def gen_config(self) -> None: # TODO: should we just take those layers out of the IO tlef? they shouldn't be necessary since we don't route on those layers libs += [ Library( - lef_file="$SKY130_SCL/lef/sky130_scl_9T.tlef", + lef_file="cache/sky130_scl_9T.tlef", + # lef_file="$SKY130_SCL/lef/sky130_scl_9T.tlef", verilog_sim="$SKY130_SCL/verilog/sky130_scl_9T.v", provides=[Provide(lib_type="technology")], ), @@ -66,7 +67,8 @@ def gen_config(self) -> None: else: raise ValueError(f"Incorrect standard cell library selection: {slib}") # Generate IO cells unless we're using the Cadence stdcell lib (see above comment about missing layers in tlef) - if slib != "sky130_scl": + # nvm we need to do this + if True: # slib != "sky130_scl": library = "sky130_fd_io" SKYWATER_LIBS = os.path.join("$SKY130A", "libs.ref", library) LIBRARY_PATH = os.path.join(SKY130A, "libs.ref", library, "lib") @@ -488,7 +490,7 @@ def post_install_script(self) -> None: if self.get_setting("technology.sky130.stdcell_library") == "sky130_fd_sc_hd": self.setup_cdl() # self.setup_verilog() - self.setup_techlef() + self.setup_techlef() self.logger.info("Loaded Sky130 Tech") def setup_cdl(self) -> None: @@ -654,21 +656,31 @@ def setup_verilog(self) -> None: # 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) - source_path = ( - setting_dir - / "libs.ref" - / self.library_name - / "techlef" - / f"{self.library_name}__nom.tlef" - ) + cache_tech_dir_path = Path(self.cache_dir) + os.makedirs(cache_tech_dir_path, exist_ok=True) + if self.get_setting("technology.sky130.stdcell_library") == "sky130_fd_sc_hd": + setting_dir = self.get_setting("technology.sky130.sky130A") + setting_dir = Path(setting_dir) + source_path = ( + setting_dir + / "libs.ref" + / self.library_name + / "techlef" + / f"{self.library_name}__nom.tlef" + ) + dest_path = cache_tech_dir_path / f"{self.library_name}__nom.tlef" + else: + setting_dir = self.get_setting("technology.sky130.sky130_scl") + setting_dir = Path(setting_dir) + source_path = ( + setting_dir + / "lef" + / "sky130_scl_9T.tlef" + ) + dest_path = cache_tech_dir_path / "sky130_scl_9T.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) - dest_path = cache_tech_dir_path / f"{self.library_name}__nom.tlef" with open(source_path, "r") as sf: with open(dest_path, "w") as df: @@ -677,7 +689,7 @@ def setup_techlef(self) -> None: ) for line in sf: df.write(line) - if line.strip() == "END pwell": + if line.strip() == "END poly": df.write(_the_tlef_edit) # syn_power seems to not take hooks from `get_tech_syn_hooks` @@ -788,6 +800,11 @@ def get_tech_lvs_hooks(self, tool_name: str) -> List[HammerToolHookAction]: "generate_lvs_run_file", calibre_lvs_blackbox_srams ) ) + pegasus_hooks.append( + HammerTool.make_post_insertion_hook( + "generate_lvs_ctl_file", pegasus_lvs_blackbox_srams + ) + ) if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": pegasus_hooks.append( @@ -841,7 +858,17 @@ def sky130_sram_names() -> List[str]: return sky130_sram_names +# the io libs (sky130a) _the_tlef_edit = """ +LAYER nwell + TYPE MASTERSLICE ; +END nwell +LAYER pwell + TYPE MASTERSLICE ; +END pwell +LAYER li1 + TYPE MASTERSLICE ; +END li1 LAYER licon TYPE CUT ; END licon @@ -1141,7 +1168,7 @@ def pegasus_lvs_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" lvs_box = "" for name in ( - SKY130Tech.sky130_sram_names() + SKY130Tech.sky130_sram_primitive_names() + SKY130Tech.sky130_sram_names() # + SKY130Tech.sky130_sram_primitive_names() ): lvs_box += f"\nlvs_black_box {name} -gray" run_file = ht.lvs_ctl_file # type: ignore From 00e2a0259846f2b196d27eda19e457790a92da0a Mon Sep 17 00:00:00 2001 From: elamdf Date: Fri, 25 Oct 2024 16:00:29 -0700 Subject: [PATCH 30/68] add scaled versions of drivers, clkbuffers for sky130_scl and add correct antenna diode cell name --- hammer/technology/sky130/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 29395ddc3..a6d9bb6b6 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -345,11 +345,11 @@ def gen_config(self) -> None: ), SpecialCell( cell_type="driver", - name=["TBUF"], + name=["TBUFX1", "TBUFX4", "TBUFX8"], input_ports=["A"], output_ports=["Y"], ), - SpecialCell(cell_type="ctsbuffer", name=["CLKBUFX2"]), + SpecialCell(cell_type="ctsbuffer", name=["CLKBUFX2","CLKBUFX4", "CLKBUFX8" ]), SpecialCell(cell_type=CellType("ctsgate"), name=["ICGX1"]), SpecialCell( cell_type=CellType("tiehicell"), name=["TIEHI"], input_ports=["Y"] @@ -881,7 +881,7 @@ def sky130_innovus_settings(ht: HammerTool) -> bool: assert isinstance(ht, TCLTool), "innovus settings can only run on TCL tools" """Settings for every tool invocation""" ht.append( - """ + f""" ########################################################## # Placement attributes [get_db -category place] @@ -922,7 +922,7 @@ def sky130_innovus_settings(ht: HammerTool) -> bool: ########################################################## #------------------------------------------------------------------------------- set_db route_design_antenna_diode_insertion 1 -set_db route_design_antenna_cell_name "sky130_fd_sc_hd__diode_2" +set_db route_design_antenna_cell_name "{"sky130_fd_sc_hd__diode_2" if ht.get_setting("technology.sky130.stdcell_library") == "sky130_fd_sc_hd" else "ANTENNA"}" set_db route_design_high_freq_search_repair true set_db route_design_detail_post_route_spread_wire true From bb038240d5815032e26e2a2a77ec2bbaa0686dea Mon Sep 17 00:00:00 2001 From: elamdf Date: Mon, 28 Oct 2024 14:23:45 -0700 Subject: [PATCH 31/68] use cached (hacked) io lef for sky130_scl to prevent routing to filler io cells --- hammer/technology/sky130/__init__.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index a6d9bb6b6..f19126513 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -122,16 +122,18 @@ def gen_config(self) -> None: file_lib = "sky130_ef_io" gds_file = cell_name + ".gds" - lef_file = os.path.join( - SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" - ) + lef_file="cache/sky130_ef_io.lef" + #lef_file = os.path.join( + #SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" + #) spice_file = os.path.join(SKYWATER_LIBS, "cdl", file_lib + ".cdl") elif "sky130_ef_io" in cell_name: file_lib = "sky130_ef_io" gds_file = file_lib + ".gds" - lef_file = os.path.join( - SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" - ) + lef_file="cache/sky130_ef_io.lef" + #lef_file = os.path.join( + #SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" + #) spice_file = os.path.join(SKYWATER_LIBS, "cdl", file_lib + ".cdl") else: file_lib = library From 0973afb78ae30de875443f146733dba3d91ec1d8 Mon Sep 17 00:00:00 2001 From: elamdf Date: Tue, 29 Oct 2024 07:41:09 -0700 Subject: [PATCH 32/68] don't use spice files for IO in sky130_scl since they're blackboxed in LVS (and this casuses import error) --- hammer/technology/sky130/__init__.py | 29 ++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index f19126513..ae46cd4d3 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -126,7 +126,10 @@ def gen_config(self) -> None: #lef_file = os.path.join( #SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" #) - spice_file = os.path.join(SKYWATER_LIBS, "cdl", file_lib + ".cdl") + if self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.pegasus" and slib == "sky130_scl": + spice_file = None + else: + spice_file = os.path.join(SKYWATER_LIBS, "cdl", file_lib + ".cdl") elif "sky130_ef_io" in cell_name: file_lib = "sky130_ef_io" gds_file = file_lib + ".gds" @@ -134,14 +137,21 @@ def gen_config(self) -> None: #lef_file = os.path.join( #SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" #) - spice_file = os.path.join(SKYWATER_LIBS, "cdl", file_lib + ".cdl") + if self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.pegasus" and slib == "sky130_scl": + spice_file = None + else: + spice_file = os.path.join(SKYWATER_LIBS, "cdl", file_lib + ".cdl") else: + if "sky130_fd_io" in cell_name and self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.pegasus" : + # going to blackbox these + spice_file = None + else: + spice_file = os.path.join( + SKYWATER_LIBS, "spice", file_lib + ".spice" + ) file_lib = library gds_file = file_lib + ".gds" lef_file = os.path.join(SKYWATER_LIBS, "lef", file_lib + ".lef") - spice_file = os.path.join( - SKYWATER_LIBS, "spice", file_lib + ".spice" - ) lib_entry = Library( nldm_liberty_file=os.path.join( @@ -923,8 +933,10 @@ def sky130_innovus_settings(ht: HammerTool) -> bool: # Routing attributes [get_db -category route] ########################################################## #------------------------------------------------------------------------------- -set_db route_design_antenna_diode_insertion 1 -set_db route_design_antenna_cell_name "{"sky130_fd_sc_hd__diode_2" if ht.get_setting("technology.sky130.stdcell_library") == "sky130_fd_sc_hd" else "ANTENNA"}" +puts "WARNING ELAM ELAM ELAM REMOVING ANTENNA DIODES FOR NOW BC THEY BREAK LVS and ARE NOT DRC CLEAN WITH CADENCE STDCELLS REMOVE MEEEEE" +set_db route_design_antenna_diode_insertion 0 +#set_db route_design_antenna_diode_insertion 1 +#set_db route_design_antenna_cell_name "{"sky130_fd_sc_hd__diode_2" if ht.get_setting("technology.sky130.stdcell_library") == "sky130_fd_sc_hd" else "ANTENNA"}" set_db route_design_high_freq_search_repair true set_db route_design_detail_post_route_spread_wire true @@ -1148,9 +1160,6 @@ def pegasus_lvs_add_130a_primitives(ht: HammerTool) -> bool: for name in SKY130Tech.sky130_sram_primitive_names(): lvs_box += f"""\nschematic_path "{name}" spice;""" # this is because otherwise lvs crashes with tons of stdcell-level pin mismatches - # TODO elam move this somewhere better - # lvs_box += f"""\nlvs_discard_pins yes;""" - # lvs_box += f"""\nlvs_expand_cell_on_error yes;""" lvs_box += f"""\nlvs_inconsistent_reduction_threshold -none;""" run_file = ht.lvs_ctl_file # type: ignore with open(run_file, "r+") as f: From 26fe2f0a7de7108ebf894c0eb472a08eee8c4390 Mon Sep 17 00:00:00 2001 From: elamdf Date: Tue, 29 Oct 2024 07:43:05 -0700 Subject: [PATCH 33/68] Revert "use cached (hacked) io lef for sky130_scl to prevent routing to filler" This reverts commit bb038240d5815032e26e2a2a77ec2bbaa0686dea. --- hammer/technology/sky130/__init__.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index ae46cd4d3..d077d4078 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -122,10 +122,9 @@ def gen_config(self) -> None: file_lib = "sky130_ef_io" gds_file = cell_name + ".gds" - lef_file="cache/sky130_ef_io.lef" - #lef_file = os.path.join( - #SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" - #) + lef_file = os.path.join( + SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" + ) if self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.pegasus" and slib == "sky130_scl": spice_file = None else: @@ -133,10 +132,9 @@ def gen_config(self) -> None: elif "sky130_ef_io" in cell_name: file_lib = "sky130_ef_io" gds_file = file_lib + ".gds" - lef_file="cache/sky130_ef_io.lef" - #lef_file = os.path.join( - #SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" - #) + lef_file = os.path.join( + SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" + ) if self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.pegasus" and slib == "sky130_scl": spice_file = None else: From 8fa0f34927577d81664446316570c2840b9e3bd2 Mon Sep 17 00:00:00 2001 From: elamdf Date: Fri, 1 Nov 2024 15:12:23 -0700 Subject: [PATCH 34/68] fix tlef hack for 130a --- hammer/technology/sky130/__init__.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index d077d4078..555bf3992 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -343,7 +343,7 @@ def gen_config(self) -> None: deck_name="pegasus_drc", path="$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl", ), - ], + ] ) elif slib == "sky130_scl": @@ -699,8 +699,12 @@ def setup_techlef(self) -> None: ) for line in sf: df.write(line) - if line.strip() == "END poly": - df.write(_the_tlef_edit) + if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": + if line.strip() == "END poly": + df.write(_the_tlef_edit + _additional_tlef_edit_for_scl) + else: + if line.strip() == "END pwell": + df.write(_the_tlef_edit) # syn_power seems to not take hooks from `get_tech_syn_hooks` # also, syn_power is called from joules so we need to add the hook to joules @@ -870,6 +874,11 @@ def sky130_sram_names() -> List[str]: # the io libs (sky130a) _the_tlef_edit = """ +LAYER licon + TYPE CUT ; +END licon +""" +_additional_tlef_edit_for_scl = """" LAYER nwell TYPE MASTERSLICE ; END nwell @@ -879,9 +888,6 @@ def sky130_sram_names() -> List[str]: LAYER li1 TYPE MASTERSLICE ; END li1 -LAYER licon - TYPE CUT ; -END licon """ From 515a0496e17b0c63db2b39ff6b322a14a646cec8 Mon Sep 17 00:00:00 2001 From: Elam Day-Friedland Date: Sun, 22 Dec 2024 20:21:02 -0800 Subject: [PATCH 35/68] add stacv2 sky130 hammer changes (#882) * efabless ring IO template + hooks * consolidate docs * add sky130_fd_io library from open_pdks, fix template IO file * fix corner cell, make met5 stripes like caravel * met5 rings now connected to clamped3 pads * add set_dont_touch on tie_hi/lo_esd output pins * Update IO file instructions * typos, move section * More clarity * updated sram22 naming convention * Update compiler to check for both PEX and schematic LIBs * lib path change * fix SRAM caches and compiler logs * patch PAD class on connect_* IO spacers * update sram cache to include latest SRAM varieties * add back all srams * fix sram compiler * add SRAM aggregated SPICE netlist for LVS * Update tech for LVS and DRC fixes * fix sram cache * Expose additional CORE pins * style fixes * remove ctsbuffer cell that breaks synthesis * TODO clean this up, flow at least works, but LVS has issues with trailing colons (yet no virtual connect) and drc has some random met1 spacing violations around srams * remove redundant code, allow for automatic overrides of library default paths with cache or env yml * add fillers with drc in innovus, this is required for a clean design * remove sram with inconsistent ss and tt lib * fixes for scl, par at least runs but with congestion issues * revert to upstream readme * include met1 in default routing layers, otherwise cadence stdcells have horrible QoR * add calibre decks to sky130_scl flow, try to blackbox io cells for pegasus run * remove dead code * readd cadence pdk part of readme * oops add newlines * remove hardcoded cache overrides * remove unreleased srams * more robust override/cache path decision making in tech init * fix extra sram cache --------- Co-authored-by: Harrison Liew Co-authored-by: Nayiri K Co-authored-by: rohanku Co-authored-by: Ethan Wu --- hammer/tech/__init__.py | 59 ++ hammer/technology/asap7/defaults.yml | 4 + hammer/technology/sky130/README.md | 5 +- hammer/technology/sky130/__init__.py | 813 +++++++++--------- hammer/technology/sky130/defaults.yml | 8 +- .../sky130/extra/efabless_template.io | 5 - .../sky130/extra/sram22/sram-cache-gen.py | 34 +- .../sky130/extra/sram22/sram-cache.json | 436 +++++++++- .../technology/sky130/extra/sram22/srams.txt | 22 + hammer/technology/sky130/sky130_lefpin.map | 3 +- hammer/technology/sky130/sram-cache.json | 458 +++++++++- .../sky130/sram_compiler/__init__.py | 36 +- hammer/vlsi/driver.py | 1 + 13 files changed, 1377 insertions(+), 507 deletions(-) create mode 100644 hammer/technology/sky130/extra/sram22/srams.txt diff --git a/hammer/tech/__init__.py b/hammer/tech/__init__.py index 7f4b9f709..768365a0b 100644 --- a/hammer/tech/__init__.py +++ b/hammer/tech/__init__.py @@ -322,6 +322,65 @@ def ensure_dirs_exist(self, path) -> None: self.logger.info('Creating directory: {}'.format(dir_name)) os.makedirs(dir_name) + def override_tech_libraries(self) -> None: + """ + Override library paths to cached or manually overriden tech collateral + """ + if not self.config.libraries: + return + + tech_module: str = self.get_setting("vlsi.core.technology") + tech_name = tech_module.split('.')[-1] + manual_overrides = {} + + # if user has broken glass, map tech filename -> manual path + if self.get_setting("vlsi.technology.manually_override_pdk_collateral"): + for lib in self.get_setting("vlsi.technology.override_libraries"): + for key, path in lib['library'].items(): + fname = os.path.basename(path) + if (key, fname) in manual_overrides: + self.logger.error("Attempted to add {(key,path)} to overrides when {manual_overrides[(key, fname)]} already exists!") + manual_overrides[(key, fname)] = path + else: + if len(self.get_setting("vlsi.technology.override_libraries")) > 0: + self.logger.warning("You've attempted to specify override libraries without enabling vlsi.technology.manually_override_pdk_collateral! collateral paths will not be overwritten") + + + used_overrides = [] + for lib in self.config.libraries: + for field_name in lib.model_fields: + if field_name.endswith("_file") or field_name == "verilog_sim": + # check if that file exists in cache, override if so + default_path = getattr(lib, field_name) + if not default_path: + continue + new_path = default_path + fname = os.path.basename(default_path) + fnames = [fname, fname.replace(".spice", ".cdl"), fname.replace(".cdl", ".spice")] + + cached_paths = set(os.path.join(self.cache_dir, f) for f in fnames) + cached_paths = [f for f in cached_paths if os.path.exists(f)] + + # only allow 1 valid cached path, otherwise ambiguous + assert len(cached_paths) <= 1, f"ambiguous cache override: {cached_paths}" + if cached_paths: + new_path = cached_paths[0] + self.logger.info( + f"overriding {default_path} with cache entry {new_path}" + ) + # prioritize manual overrides over cache + if (field_name, fname) in manual_overrides: + used_overrides.append((field_name, fname)) + new_path = manual_overrides[(field_name, fname)] + self.logger.info( + f"overriding {default_path} with manual override {new_path}" + ) + + setattr(lib, field_name, new_path) + unused_overrides = [k for k in manual_overrides.keys() if k not in used_overrides] + if unused_overrides: + self.logger.warning(f"Unused tech collateral overrides: {unused_overrides}") + # hammer-vlsi properties. # TODO: deduplicate/put these into an interface to share with HammerTool? @property diff --git a/hammer/technology/asap7/defaults.yml b/hammer/technology/asap7/defaults.yml index ba5f45d19..0583029df 100644 --- a/hammer/technology/asap7/defaults.yml +++ b/hammer/technology/asap7/defaults.yml @@ -41,6 +41,10 @@ vlsi: routing_layers: [2, 7] + # WARNING: BREAK GLASS flag to override tech collateral with the paths provided in `override_libraries` + technology.manually_override_pdk_collateral: false + technology.override_libraries: [] # hacked collateral files to be used instead of default tech files with the same name + technology.core: stackup: "asap7_3Ma_2Mb_2Mc_2Md" # This key should exist in the stackups list in the tech json diff --git a/hammer/technology/sky130/README.md b/hammer/technology/sky130/README.md index 2a630ad01..49158f688 100644 --- a/hammer/technology/sky130/README.md +++ b/hammer/technology/sky130/README.md @@ -9,6 +9,8 @@ PDK Setup The Skywater 130nm PDK files are located in a repo called [skywater-pdk](https://github.com/google/skywater-pdk/). A tool called [Open-PDKs (open_pdks)](https://github.com/RTimothyEdwards/open_pdks/) was developed to generate all the files typically found in a PDK. Open-PDKs uses the contents in `skywater-pdk`, and outputs files to a directory called `sky130A`. +This will take around 40-50GB of disk space as of September 2024. + ### PDK Install ```shell @@ -23,7 +25,9 @@ export PATH=$PREFIX/.conda-signoff/bin:$PATH # clone required repos git clone https://github.com/google/skywater-pdk.git +# rev 7198cf647113f56041e02abf3eb623692820c5e1 git clone https://github.com/RTimothyEdwards/open_pdks.git +# rev 320597ea84b2816eb2fcc4fbe10c3874f19c92fc # install Sky130 PDK via Open-PDKs # we disable some install steps to save time @@ -50,7 +54,6 @@ technology.sky130.sky130A: "/share/pdk/sky130A" Cadence has also created a PDK and standard cell library based on the original open PDK, and optimized for use with Cadence tools. Usage of this PDK is purely optional, but enables DRC/LVS with Pegasus, and IR drop analysis with Voltus. You can download them from [here](https://support.cadence.com/apex/ArticleAttachmentPortal?id=a1Od000000051TqEAI&pageName=ArticleContent) (Cadence Support account is required). After downloading and untar-ing the packages, point to the location of this install: - ```yaml technology.sky130.sky130_cds: "" technology.sky130.sky130_scl: "" diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 555bf3992..55a76bd60 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -2,28 +2,41 @@ # # See LICENSE for licence details. -import sys -import re +import functools +import importlib +import json import os +import re import shutil from pathlib import Path -from typing import NamedTuple, List, Optional, Tuple, Dict, Set, Any -import importlib -import json -import functools - -from hammer.tech import * +from typing import List + +from hammer.tech import ( + Corner, + Decimal, + DRCDeck, + HammerTechnology, + Library, + LVSDeck, + Metal, + PathPrefix, + Provide, + Site, + Stackup, + Supplies, + TechJSON, +) +from hammer.tech.specialcells import CellType, SpecialCell +from hammer.utils import LEFUtils from hammer.vlsi import ( - HammerTool, - HammerPlaceAndRouteTool, - TCLTool, HammerDRCTool, HammerLVSTool, + HammerPlaceAndRouteTool, + HammerTool, HammerToolHookAction, HierarchicalMode, + TCLTool, ) -from hammer.utils import LEFUtils -from hammer.vlsi.hammer_vlsi_impl import HammerSimTool class SKY130Tech(HammerTechnology): @@ -37,133 +50,33 @@ def gen_config(self) -> None: slib = self.get_setting("technology.sky130.stdcell_library") SKY130A = self.get_setting("technology.sky130.sky130A") SKY130_CDS = self.get_setting("technology.sky130.sky130_cds") - SKY130_CDS_LIB = self.get_setting("technology.sky130.sky130_scl") + SKY130_SCL = self.get_setting("technology.sky130.sky130_scl") # Common tech LEF and IO cell spice netlists libs = [] if slib == "sky130_fd_sc_hd": libs += [ Library( - spice_file="$SKY130A/libs.ref/sky130_fd_io/spice/sky130_ef_io.spice", - provides=[Provide(lib_type="IO library")], - ), - Library( - lef_file="cache/sky130_fd_sc_hd__nom.tlef", - verilog_sim="cache/primitives.v", + lef_file=os.path.join( + SKY130A, + "libs.ref/sky130_fd_sc_hd/techlef/sky130_fd_sc_hd__nom.tlef", + ), + verilog_sim=os.path.join( + SKY130A, "libs.ref/sky130_fd_sc_hd/verilog/primitives.v" + ), provides=[Provide(lib_type="technology")], ), ] elif slib == "sky130_scl": - # Since the cadence (v0.0.3) tlef doesn't have nwell/pwell/li1, we can't use the sky130A IO libs. - # TODO: should we just take those layers out of the IO tlef? they shouldn't be necessary since we don't route on those layers libs += [ Library( - lef_file="cache/sky130_scl_9T.tlef", - # lef_file="$SKY130_SCL/lef/sky130_scl_9T.tlef", - verilog_sim="$SKY130_SCL/verilog/sky130_scl_9T.v", + lef_file=os.path.join(SKY130_SCL, "/lef/sky130_scl_9T.tlef"), + verilog_sim=os.path.join(SKY130_SCL, "/verilog/sky130_scl_9T.v"), provides=[Provide(lib_type="technology")], ), ] else: raise ValueError(f"Incorrect standard cell library selection: {slib}") - # Generate IO cells unless we're using the Cadence stdcell lib (see above comment about missing layers in tlef) - # nvm we need to do this - if True: # slib != "sky130_scl": - library = "sky130_fd_io" - SKYWATER_LIBS = os.path.join("$SKY130A", "libs.ref", library) - LIBRARY_PATH = os.path.join(SKY130A, "libs.ref", library, "lib") - lib_corner_files = os.listdir(LIBRARY_PATH) - lib_corner_files.sort() - for cornerfilename in lib_corner_files: - # Skip versions with no internal power - if "nointpwr" in cornerfilename: - continue - - tmp = cornerfilename.replace(".lib", "") - # Split into cell, and corner strings - # Resulting list if only one ff/ss/tt in name: [, , , , ] - # Resulting list if ff_ff/ss_ss/tt_tt in name: [, , , , '', , , , ] - split_cell_corner = re.split("_(ff)|_(ss)|_(tt)", tmp) - cell_name = split_cell_corner[0] - process = split_cell_corner[1:-1] - temp_volt = split_cell_corner[-1].split("_")[1:] - - # Filter out cross corners (e.g ff_ss or ss_ff) - if len(process) > 3: - if not functools.reduce( - lambda x, y: x and y, - map(lambda p, q: p == q, process[0:3], process[4:]), - True, - ): - continue - # Determine actual corner - speed = next(c for c in process if c is not None).replace("_", "") - if speed == "ff": - speed = "fast" - if speed == "tt": - speed = "typical" - if speed == "ss": - speed = "slow" - - temp = temp_volt[0] - temp = temp.replace("n", "-") - temp = temp.split("C")[0] + " C" - - vdd = (".").join(temp_volt[1].split("v")) + " V" - # Filter out IO/analog voltages that are not high voltage - if temp_volt[2].startswith("1"): - continue - if len(temp_volt) == 4: - if temp_volt[3].startswith("1"): - continue - - # gpiov2_pad_wrapped has separate GDS - if cell_name == "sky130_ef_io__gpiov2_pad_wrapped": - - file_lib = "sky130_ef_io" - gds_file = cell_name + ".gds" - lef_file = os.path.join( - SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" - ) - if self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.pegasus" and slib == "sky130_scl": - spice_file = None - else: - spice_file = os.path.join(SKYWATER_LIBS, "cdl", file_lib + ".cdl") - elif "sky130_ef_io" in cell_name: - file_lib = "sky130_ef_io" - gds_file = file_lib + ".gds" - lef_file = os.path.join( - SKY130A, "libs.ref", library, "lef", "sky130_ef_io.lef" - ) - if self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.pegasus" and slib == "sky130_scl": - spice_file = None - else: - spice_file = os.path.join(SKYWATER_LIBS, "cdl", file_lib + ".cdl") - else: - if "sky130_fd_io" in cell_name and self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.pegasus" : - # going to blackbox these - spice_file = None - else: - spice_file = os.path.join( - SKYWATER_LIBS, "spice", file_lib + ".spice" - ) - file_lib = library - gds_file = file_lib + ".gds" - lef_file = os.path.join(SKYWATER_LIBS, "lef", file_lib + ".lef") - - lib_entry = Library( - nldm_liberty_file=os.path.join( - SKYWATER_LIBS, "lib", cornerfilename - ), - verilog_sim=os.path.join(SKYWATER_LIBS, "verilog", file_lib + ".v"), - lef_file=lef_file, - spice_file=spice_file, - gds_file=os.path.join(SKYWATER_LIBS, "gds", gds_file), - corner=Corner(nmos=speed, pmos=speed, temperature=temp), - supplies=Supplies(VDD=vdd, GND="0 V"), - provides=[Provide(lib_type=cell_name, vt="RVT")], - ) - libs.append(lib_entry) # Stdcell library-dependent lists stackups = [] # type: List[Stackup] @@ -171,9 +84,11 @@ def gen_config(self) -> None: dont_use = [] # type: List[Cell] spcl_cells = [] # type: List[SpecialCell] + # base path -> list of corners + lib_corner_files = {} + # Select standard cell libraries if slib == "sky130_fd_sc_hd": - phys_only = [ "sky130_fd_sc_hd__tap_1", "sky130_fd_sc_hd__tap_2", @@ -237,9 +152,11 @@ def gen_config(self) -> None: input_ports=["A"], output_ports=["X"], ), - SpecialCell( - cell_type=CellType("ctsbuffer"), name=["sky130_fd_sc_hd__clkbuf_1"] - ), + # this breaks synthesis with a complaint about "Cannot perform synthesis because libraries do not have usable inverters." from Genus. + # note that innovus still recognizes and uses this cell as a buffer + # SpecialCell( + # cell_type=CellType("ctsbuffer"), name=["sky130_fd_sc_hd__clkbuf_1"] + # ), ] # Generate standard cell library @@ -248,56 +165,11 @@ def gen_config(self) -> None: # scl vs 130a have different site names sites = None - SKYWATER_LIBS = os.path.join("$SKY130A", "libs.ref", library) - LIBRARY_PATH = os.path.join(SKY130A, "libs.ref", library, "lib") - lib_corner_files = os.listdir(LIBRARY_PATH) - lib_corner_files.sort() - for cornerfilename in lib_corner_files: - if not ( - "sky130" in cornerfilename - ): # cadence doesn't use the lib name in their corner libs - continue - if "ccsnoise" in cornerfilename: - continue # ignore duplicate corner.lib/corner_ccsnoise.lib files - - tmp = cornerfilename.replace(".lib", "") - if tmp + "_ccsnoise.lib" in lib_corner_files: - cornerfilename = ( - tmp + "_ccsnoise.lib" - ) # use ccsnoise version of lib file - - cornername = tmp.split("__")[1] - cornerparts = cornername.split("_") - - speed = cornerparts[0] - if speed == "ff": - speed = "fast" - if speed == "tt": - speed = "typical" - if speed == "ss": - speed = "slow" - - temp = cornerparts[1] - temp = temp.replace("n", "-") - temp = temp.split("C")[0] + " C" - - vdd = cornerparts[2] - vdd = vdd.split("v")[0] + "." + vdd.split("v")[1] + " V" - - lib_entry = Library( - nldm_liberty_file=os.path.join( - SKYWATER_LIBS, "lib", cornerfilename - ), - verilog_sim=os.path.join("cache", library + ".v"), - lef_file=os.path.join(SKYWATER_LIBS, "lef", library + ".lef"), - spice_file=os.path.join("cache", library + ".cdl"), - gds_file=os.path.join(SKYWATER_LIBS, "gds", library + ".gds"), - corner=Corner(nmos=speed, pmos=speed, temperature=temp), - supplies=Supplies(VDD=vdd, GND="0 V"), - provides=[Provide(lib_type="stdcell", vt="RVT")], - ) - - libs.append(lib_entry) + STDCELL_LIBRARY_BASE_PATH = os.path.join(SKY130A, "libs.ref", library) + lib_corner_files[STDCELL_LIBRARY_BASE_PATH] = os.listdir( + os.path.join(STDCELL_LIBRARY_BASE_PATH, "lib") + ) + lib_corner_files[STDCELL_LIBRARY_BASE_PATH].sort() # Generate stackup tlef_path = os.path.join( @@ -326,27 +198,26 @@ def gen_config(self) -> None: path="$SKY130_CDS/Sky130_LVS/sky130.lvs.pvl", ), ] - drc_decks = ( - [ - DRCDeck( - tool_name="calibre", - deck_name="calibre_drc", - path="$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules", - ), - DRCDeck( - tool_name="klayout", - deck_name="klayout_drc", - path="$SKY130A/libs.tech/klayout/drc/sky130A.lydrc", - ), - DRCDeck( - tool_name="pegasus", - deck_name="pegasus_drc", - path="$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl", - ), - ] - ) + drc_decks = [ + DRCDeck( + tool_name="calibre", + deck_name="calibre_drc", + path="$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules", + ), + DRCDeck( + tool_name="klayout", + deck_name="klayout_drc", + path="$SKY130A/libs.tech/klayout/drc/sky130A.lydrc", + ), + DRCDeck( + tool_name="pegasus", + deck_name="pegasus_drc", + path="$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl", + ), + ] elif slib == "sky130_scl": + # note: you need to manually include io cells in design.yml if using scl # The cadence PDK (as of version 0.0.3) doesn't seem to have tap nor decap cells, so par won't run (and if we forced it to, lvs would fail) spcl_cells = [ @@ -355,12 +226,17 @@ def gen_config(self) -> None: ), SpecialCell( cell_type="driver", - name=["TBUFX1", "TBUFX4", "TBUFX8"], + name=["TBUFX1", "TBUFX4", "TBUFX8"], input_ports=["A"], output_ports=["Y"], ), - SpecialCell(cell_type="ctsbuffer", name=["CLKBUFX2","CLKBUFX4", "CLKBUFX8" ]), - SpecialCell(cell_type=CellType("ctsgate"), name=["ICGX1"]), + # this breaks synthesis with a complaint about "Cannot perform synthesis because libraries do not have usable inverters." from Genus. + # note that innovus still recognizes and uses this cell as a buffer + # added for par with a hook `set_cts_base_cells` + # SpecialCell( + # cell_type="ctsbuffer", name=["CLKBUFX2", "CLKBUFX4", "CLKBUFX8"] + # ), + # SpecialCell(cell_type=CellType("ctsgate"), name=["ICGX1"]), SpecialCell( cell_type=CellType("tiehicell"), name=["TIEHI"], input_ports=["Y"] ), @@ -371,65 +247,16 @@ def gen_config(self) -> None: # Generate standard cell library library = slib - - LIBRARY_PATH = os.path.join(SKY130_CDS_LIB, "lib") - lib_corner_files = os.listdir(LIBRARY_PATH) - lib_corner_files.sort() - for cornerfilename in lib_corner_files: - if not ( - "sky130" in cornerfilename - ): # cadence doesn't use the lib name in their corner libs - continue - if "ccsnoise" in cornerfilename: - continue # ignore duplicate corner.lib/corner_ccsnoise.lib files - - tmp = cornerfilename.replace(".lib", "") - if tmp + "_ccsnoise.lib" in lib_corner_files: - cornerfilename = ( - tmp + "_ccsnoise.lib" - ) # use ccsnoise version of lib file - - cornername = tmp.replace("sky130_", "") - cornerparts = cornername.split("_") - - # Hardcode corner annotations since they don't exactly match the sky130a - speed = cornerparts[0] - vdd = "" - temp = "" - if speed == "ff": - temp = "-40 C" - vdd = "1.95 V" - speed = "fast" - if speed == "tt": - vdd = "1.80 V" - temp = "25 C" - speed = "typical" - if speed == "ss": - vdd = "1.60 V" - speed = "slow" - temp = "100 C" - - lib_entry = Library( - nldm_liberty_file=os.path.join( - SKY130_CDS_LIB, "lib", cornerfilename - ), - verilog_sim=os.path.join( - SKY130_CDS_LIB, "verilog", library + "_9T.v" - ), - lef_file=os.path.join(SKY130_CDS_LIB, "lef", library + "_9T.lef"), - spice_file=os.path.join(SKY130_CDS_LIB, "cdl", library + "_9T.cdl"), - gds_file=os.path.join(SKY130_CDS_LIB, "gds", library + "_9T.gds"), - corner=Corner(nmos=speed, pmos=speed, temperature=temp), - supplies=Supplies(VDD=vdd, GND="0 V"), - provides=[Provide(lib_type="stdcell", vt="RVT")], - ) - - libs.append(lib_entry) + STDCELL_LIBRARY_BASE_PATH = SKY130_SCL + lib_corner_files[STDCELL_LIBRARY_BASE_PATH] = os.listdir( + os.path.join(STDCELL_LIBRARY_BASE_PATH, "lib") + ) + lib_corner_files[STDCELL_LIBRARY_BASE_PATH].sort() # Generate stackup metals = [] # type: List[Metal] - tlef_path = os.path.join(SKY130_CDS_LIB, "lef", f"{slib}_9T.tlef") + tlef_path = os.path.join(SKY130_SCL, "lef", f"{slib}_9T.tlef") metals = list( map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path)) ) @@ -444,9 +271,6 @@ def gen_config(self) -> None: ] lvs_decks = [ - # LVSDeck(tool_name="calibre", deck_name="calibre_lvs", - # path="$SKY130_NDA/s8/V2.0.1/LVS/Calibre/lvsRules_s8"), - # seems like cadence just hasthis hardcoded lvs deck name? LVSDeck( tool_name="pegasus", deck_name="pegasus_lvs", @@ -454,18 +278,162 @@ def gen_config(self) -> None: ) ] drc_decks = [ + DRCDeck( + tool_name="calibre", + deck_name="calibre_drc", + path="$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules", + ), DRCDeck( tool_name="pegasus", deck_name="pegasus_drc", path=os.path.join( SKY130_CDS, "Sky130_DRC", "sky130_rev_0.0_1.0.drc.pvl" ), - ) + ), ] else: raise ValueError(f"Incorrect standard cell library selection: {slib}") + # add skywater io cells + io_library_base_path = os.path.join(SKY130A, "libs.ref", "sky130_fd_io") + lib_corner_files[io_library_base_path] = os.listdir( + os.path.join(io_library_base_path, "lib") + ) + + for library_base_path, cornerfiles in lib_corner_files.items(): + for cornerfilename in cornerfiles: + if "sky130" not in cornerfilename or "#" in cornerfilename: + # cadence doesn't use the lib name in their corner libs + # also skip random temp files sometimes included in sky130_scl + continue + if "ccsnoise" in cornerfilename: + continue # ignore duplicate corner.lib/corner_ccsnoise.lib files + + tmp = cornerfilename.replace(".lib", "").strip("_nldm") + if tmp + "_ccsnoise.lib" in lib_corner_files: + cornerfilename = ( + tmp + "_ccsnoise.lib" + ) # use ccsnoise version of lib file + + # different naming conventions + if "sky130A" in library_base_path: + split_cell_corner = re.split("_(ff)|_(ss)|_(tt)", tmp) + cell_name = split_cell_corner[0] + library = tmp.split("__")[0] + process = split_cell_corner[1:-1] + temp_volt = split_cell_corner[-1].split("_")[1:] + + # Filter out cross corners (e.g ff_ss or ss_ff) + if len(process) > 3: + if not functools.reduce( + lambda x, y: x and y, + map(lambda p, q: p == q, process[0:3], process[4:]), + True, + ): + continue + # Determine actual corner + speed = next(c for c in process if c is not None).replace("_", "") + temp = temp_volt[0] + temp = temp.replace("n", "-") + temp = temp.split("C")[0] + " C" + + vdd = (".").join(temp_volt[1].split("v")) + " V" + if speed == "ff": + speed = "fast" + elif speed == "tt": + speed = "typical" + elif speed == "ss": + speed = "slow" + else: + self.logger.info( + "Skipping lib with unsupported corner: {}".format(speed) + ) + continue + else: + library = "sky130_scl_9T" + _, speed, vdd, temp = tmp.split("_") + + # force equivalent operating conditions for speed, since they're different between sky130a and scl + if speed == "ff": + temp = "-40 C" + vdd = "1.95 V" + speed = "fast" + if speed == "tt": + vdd = "1.80 V" + temp = "25 C" + speed = "typical" + if speed == "ss": + vdd = "1.60 V" + speed = "slow" + temp = "100 C" + + cdl_path = os.path.join(library_base_path, "cdl", library + ".cdl") + spice_path = os.path.join(library_base_path, "spice", library + ".spice") + + + # just prioritize spice, arbitrary choice + #assert not (os.path.exists(cdl_path) and os.path.exists(spice_path)), "both spice and cdl netlists exist! this is ambiguous :(" + + netlist_path = ( + spice_path + if os.path.exists(spice_path) + else cdl_path + ) + + lib_entry = Library( + nldm_liberty_file=os.path.join( + library_base_path, "lib", cornerfilename + ), + verilog_sim=os.path.join( + SKY130_SCL, + "verilog", + library + "_9T.v" if slib == "sky130_scl" else ".v", + ), + lef_file=os.path.join(library_base_path, "lef", library + ".lef"), + spice_file=netlist_path, + gds_file=os.path.join(library_base_path, "gds", library + ".gds"), + corner=Corner(nmos=speed, pmos=speed, temperature=temp), + supplies=Supplies(VDD=vdd, GND="0 V"), + provides=[Provide(lib_type="stdcell", vt="RVT")], + ) + libs.append(lib_entry) + + if "sky130_fd_io" in library_base_path: + # these are seperate from the io gds + extra_gds_files = [ + "sky130_ef_io__analog.gds", + "sky130_ef_io__disconnect_vccd_slice_5um.gds", + "sky130_ef_io__gpiov2_pad_wrapped.gds", + "sky130_ef_io__bare_pad.gds", + "sky130_ef_io__disconnect_vdda_slice_5um.gds", + "sky130_ef_io__connect_vcchib_vccd_and_vswitch_vddio_slice_20um.gds", + "sky130_ef_io.gds", + ] + for extra_gds_file in extra_gds_files: + lib_entry = Library( + nldm_liberty_file=os.path.join( + library_base_path, "lib", cornerfilename + ), + verilog_sim=os.path.join( + SKY130_SCL, + "verilog", + library + "_9T.v" if slib == "sky130_scl" else ".v", + ), + lef_file=os.path.join( + library_base_path, "lef", library + ".lef" + ), + spice_file=netlist_path, + gds_file=os.path.join( + library_base_path, "gds", extra_gds_file + ), + corner=Corner(nmos=speed, pmos=speed, temperature=temp), + supplies=Supplies(VDD=vdd, GND="0 V"), + provides=[Provide(lib_type="stdcell", vt="RVT")], + ) + libs.append(lib_entry) + + # TODO rename this to TechConfig or something, since we don';t use json self.config = TechJSON( name="Skywater 130nm Library", grid_unit="0.001", @@ -499,9 +467,10 @@ def post_install_script(self) -> None: ) if self.get_setting("technology.sky130.stdcell_library") == "sky130_fd_sc_hd": self.setup_cdl() - # self.setup_verilog() + self.setup_verilog() self.setup_techlef() - self.logger.info("Loaded Sky130 Tech") + # self.setup_io_lefs() + print("Loaded Sky130 Tech") def setup_cdl(self) -> None: """Copy and hack the cdl, replacing pfet_01v8_hvt/nfet_01v8 with @@ -524,14 +493,15 @@ def setup_cdl(self) -> None: dest_path = cache_tech_dir_path / f"{self.library_name}.cdl" # device names expected in LVS decks - pmos = "pfet_01v8_hvt" - nmos = "nfet_01v8" if self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.calibre": pmos = "phighvt" nmos = "nshort" elif self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.netgen": pmos = "sky130_fd_pr__pfet_01v8_hvt" nmos = "sky130_fd_pr__nfet_01v8" + else: + shutil.copy2(source_path, dest_path) + return with open(source_path, "r") as sf: with open(dest_path, "w") as df: @@ -581,67 +551,6 @@ def setup_verilog(self) -> None: ) df.write(line) - # Additionally hack out the specifies - sl = [] - with open(dest_path, "r") as sf: - sl = sf.readlines() - - # Find timing declaration - start_idx = [ - idx - for idx, line in enumerate(sl) - if "`ifndef SKY130_FD_SC_HD__LPFLOW_BLEEDER_1_TIMING_V" in line - ][0] - - # Search for the broken statement - search_range = range(start_idx + 1, len(sl)) - broken_specify_idx = len(sl) - 1 - broken_substr = "(SHORT => VPWR) = (0:0:0,0:0:0,0:0:0,0:0:0,0:0:0,0:0:0);" - - broken_specify_idx = [ - idx for idx in search_range if broken_substr in sl[idx] - ][0] - endif_idx = [idx for idx in search_range if "`endif" in sl[idx]][0] - - # Now, delete all the specify statements if specify exists before an endif. - if broken_specify_idx < endif_idx: - self.logger.info("Removing incorrectly formed specify block.") - cell_def_range = range(start_idx + 1, endif_idx) - start_specify_idx = [ - idx for idx in cell_def_range if "specify" in sl[idx] - ][0] - end_specify_idx = [ - idx for idx in cell_def_range if "endspecify" in sl[idx] - ][0] - sl[start_specify_idx : end_specify_idx + 1] = [] # Dice - - # Deal with the nonexistent net tactfully (don't code in brittle replacements) - self.logger.info("Fixing broken net references with select specify blocks.") - pattern = r"^\s*wire SLEEP.*B.*delayed;" - capture_pattern = r".*(SLEEP.*?B.*?delayed).*" - pattern_idx = [ - (idx, re.findall(capture_pattern, value)[0]) - for idx, value in enumerate(sl) - if re.search(pattern, value) - ] - for list_idx, pattern_tuple in enumerate(pattern_idx): - if list_idx != len(pattern_idx) - 1: - search_range = range(pattern_tuple[0] + 1, pattern_idx[list_idx + 1][0]) - else: - search_range = range(pattern_tuple[0] + 1, len(sl)) - for idx in search_range: - list = re.findall(capture_pattern, sl[idx]) - for elem in list: - if elem != pattern_tuple[1]: - sl[idx] = sl[idx].replace(elem, pattern_tuple[1]) - self.logger.info( - f"Incorrect reference `{elem}` to be replaced with: `{pattern_tuple[1]}` on raw line {idx}." - ) - - # Write back into destination - with open(dest_path, "w") as df: - df.writelines(sl) - # primitives.v source_path = ( setting_dir / "libs.ref" / self.library_name / "verilog" / "primitives.v" @@ -679,19 +588,14 @@ def setup_techlef(self) -> None: / f"{self.library_name}__nom.tlef" ) dest_path = cache_tech_dir_path / f"{self.library_name}__nom.tlef" - else: + else: setting_dir = self.get_setting("technology.sky130.sky130_scl") setting_dir = Path(setting_dir) - source_path = ( - setting_dir - / "lef" - / "sky130_scl_9T.tlef" - ) + source_path = setting_dir / "lef" / "sky130_scl_9T.tlef" dest_path = cache_tech_dir_path / "sky130_scl_9T.tlef" if not source_path.exists(): raise FileNotFoundError(f"Tech-LEF not found: {source_path}") - with open(source_path, "r") as sf: with open(dest_path, "w") as df: self.logger.info( @@ -699,9 +603,12 @@ def setup_techlef(self) -> None: ) for line in sf: df.write(line) - if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": + if ( + self.get_setting("technology.sky130.stdcell_library") + == "sky130_scl" + ): if line.strip() == "END poly": - df.write(_the_tlef_edit + _additional_tlef_edit_for_scl) + df.write(_additional_tlef_edit_for_scl + _the_tlef_edit) else: if line.strip() == "END pwell": df.write(_the_tlef_edit) @@ -749,6 +656,58 @@ def enable_scl_clk_gating_cell_hook(ht: HammerTool) -> bool: # hooks['genus'].append(HammerTool.make_removal_hook("add_tieoffs")) return hooks.get(tool_name, []) + # Power pins for clamps must be CLASS CORE + def setup_io_lefs(self) -> None: + sky130A_path = Path(self.get_setting("technology.sky130.sky130A")) + source_path = ( + sky130A_path / "libs.ref" / "sky130_fd_io" / "lef" / "sky130_ef_io.lef" + ) + if not source_path.exists(): + raise FileNotFoundError(f"IO LEF not found: {source_path}") + + cache_tech_dir_path = Path(self.cache_dir) + os.makedirs(cache_tech_dir_path, exist_ok=True) + dest_path = cache_tech_dir_path / "sky130_ef_io.lef" + + with open(source_path, "r") as sf: + with open(dest_path, "w") as df: + self.logger.info( + "Modifying IO LEF: {} -> {}".format(source_path, dest_path) + ) + sl = sf.readlines() + for net in ["VCCD1", "VSSD1", "VDDA", "VSSA", "VSSIO"]: + start = [idx for idx, line in enumerate(sl) if "PIN " + net in line] + end = [idx for idx, line in enumerate(sl) if "END " + net in line] + intervals = zip(start, end) + for intv in intervals: + port_idx = [ + idx + for idx, line in enumerate(sl[intv[0] : intv[1]]) + if "PORT" in line and "met3" in sl[intv[0] + idx + 1] + ] + for idx in port_idx: + sl[intv[0] + idx] = sl[intv[0] + idx].replace( + "PORT", "PORT\n CLASS CORE ;" + ) + # force class to spacer + # TODO: the disconnect_* slices are also broken like this, but we're not using them + start = [ + idx + for idx, line in enumerate(sl) + if "MACRO sky130_ef_io__connect_vcchib_vccd_and_vswitch_vddio_slice_20um" + in line + ] + sl[start[0] + 1] = sl[start[0] + 1].replace("AREAIO", "SPACER") + + for idx, line in enumerate(sl): + if "PIN OUT" in line: + sl[idx + 1].replace( + "DIRECTION INPUT ;", + "DIRECTION INPUT ;\n ANTENNAGATEAREA 1.529 LAYER met3 ;", + ) + + df.writelines(sl) + def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]: hooks = { "innovus": [ @@ -763,11 +722,15 @@ def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]: } # there are no cap/decap cells in the cadence stdcell library as of version 0.0.3, so we can't do things that reference them if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": - - hooks["innovus"].append( - HammerTool.make_replacement_hook( - "power_straps", power_rail_straps_no_tapcells - ) + hooks["innovus"].extend( + [ + HammerTool.make_replacement_hook( + "power_straps", power_rail_straps_no_tapcells + ), + HammerTool.make_pre_insertion_hook( + "clock_tree", set_cts_base_cells + ), + ] ) else: hooks["innovus"].append( @@ -792,15 +755,21 @@ def get_tech_drc_hooks(self, tool_name: str) -> List[HammerToolHookAction]: "generate_drc_ctl_file", pegasus_drc_blackbox_srams ) ) + pegasus_hooks.append( + HammerTool.make_post_insertion_hook( + "generate_drc_ctl_file", pegasus_drc_blackbox_io_cells + ) + ) hooks = {"calibre": calibre_hooks, "pegasus": pegasus_hooks} return hooks.get(tool_name, []) def get_tech_lvs_hooks(self, tool_name: str) -> List[HammerToolHookAction]: - calibre_hooks = [ - HammerTool.make_post_insertion_hook( - "generate_lvs_run_file", setup_calibre_lvs_deck - ) - ] + # calibre_hooks = [ + # HammerTool.make_post_insertion_hook( + # "generate_lvs_run_file", setup_calibre_lvs_deck + # ) + # ] + calibre_hooks = [] pegasus_hooks = [] if self.use_sram22: calibre_hooks.append( @@ -872,13 +841,18 @@ def sky130_sram_names() -> List[str]: return sky130_sram_names +## string constants # the io libs (sky130a) _the_tlef_edit = """ +LAYER AREAIDLD + TYPE MASTERSLICE ; +END AREAIDLD + LAYER licon TYPE CUT ; END licon """ -_additional_tlef_edit_for_scl = """" +_additional_tlef_edit_for_scl = """ LAYER nwell TYPE MASTERSLICE ; END nwell @@ -890,6 +864,20 @@ def sky130_sram_names() -> List[str]: END li1 """ +LVS_DECK_INSERT_LINES = """ +LVS FILTER D OPEN SOURCE +LVS FILTER D OPEN LAYOUT +""" + +LVS_DECK_SCRUB_LINES = [ + "VIRTUAL CONNECT REPORT", + "SOURCE PRIMARY", + "SOURCE SYSTEM SPICE", + "SOURCE PATH", + "ERC", + "LVS REPORT", +] + # various Innovus database settings def sky130_innovus_settings(ht: HammerTool) -> bool: @@ -911,6 +899,7 @@ def sky130_innovus_settings(ht: HammerTool) -> bool: set_db place_global_solver_effort high set_db place_detail_check_cut_spacing true set_db place_global_cong_effort high +set_db add_fillers_with_drc false ########################################################## # Optimization attributes [get_db -category opt] @@ -937,10 +926,8 @@ def sky130_innovus_settings(ht: HammerTool) -> bool: # Routing attributes [get_db -category route] ########################################################## #------------------------------------------------------------------------------- -puts "WARNING ELAM ELAM ELAM REMOVING ANTENNA DIODES FOR NOW BC THEY BREAK LVS and ARE NOT DRC CLEAN WITH CADENCE STDCELLS REMOVE MEEEEE" -set_db route_design_antenna_diode_insertion 0 -#set_db route_design_antenna_diode_insertion 1 -#set_db route_design_antenna_cell_name "{"sky130_fd_sc_hd__diode_2" if ht.get_setting("technology.sky130.stdcell_library") == "sky130_fd_sc_hd" else "ANTENNA"}" +set_db route_design_antenna_diode_insertion 1 +set_db route_design_antenna_cell_name "{"sky130_fd_sc_hd__diode_2" if ht.get_setting("technology.sky130.stdcell_library") == "sky130_fd_sc_hd" else "ANTENNA"}" set_db route_design_high_freq_search_repair true set_db route_design_detail_post_route_spread_wire true @@ -1004,58 +991,16 @@ def sky130_add_endcaps(ht: HammerTool) -> bool: return True -def efabless_ring_io(ht: HammerTool) -> bool: - assert isinstance(ht, HammerPlaceAndRouteTool), "IO ring instantiation only for par" - assert isinstance(ht, TCLTool), "IO ring instantiation can only run on TCL tools" - io_file = ht.get_setting("technology.sky130.io_file") - ht.append(f"read_io_file {io_file} -no_die_size_adjust") - p_nets = list(map(lambda s: s.name, ht.get_independent_power_nets())) - g_nets = list(map(lambda s: s.name, ht.get_independent_ground_nets())) - ht.append( - f""" -# Global net connections -connect_global_net VDDA -type pg_pin -pin_base_name VDDA -verbose -connect_global_net VDDIO -type pg_pin -pin_base_name VDDIO* -verbose -connect_global_net {p_nets[0]} -type pg_pin -pin_base_name VCCD* -verbose -connect_global_net {p_nets[0]} -type pg_pin -pin_base_name VCCHIB -verbose -connect_global_net {p_nets[0]} -type pg_pin -pin_base_name VSWITCH -verbose -connect_global_net {g_nets[0]} -type pg_pin -pin_base_name VSSA -verbose -connect_global_net {g_nets[0]} -type pg_pin -pin_base_name VSSIO* -verbose -connect_global_net {g_nets[0]} -type pg_pin -pin_base_name VSSD* -verbose - """ - ) - ht.append( - """ -# IO fillers -set io_fillers {sky130_ef_io__connect_vcchib_vccd_and_vswitch_vddio_slice_20um sky130_ef_io__com_bus_slice_10um sky130_ef_io__com_bus_slice_5um sky130_ef_io__com_bus_slice_1um} -add_io_fillers -prefix IO_FILLER -io_ring 1 -cells $io_fillers -side top -filler_orient r0 -add_io_fillers -prefix IO_FILLER -io_ring 1 -cells $io_fillers -side right -filler_orient r270 -add_io_fillers -prefix IO_FILLER -io_ring 1 -cells $io_fillers -side bottom -filler_orient r180 -add_io_fillers -prefix IO_FILLER -io_ring 1 -cells $io_fillers -side left -filler_orient r90 -# Fix placement -set io_filler_insts [get_db insts IO_FILLER_*] -set_db $io_filler_insts .place_status fixed - """ - ) - # An offset of 40um is used to place the core ring inside the core area. It - # can be decreased down to 5um as desired, but will require additional - # routing / settings to connect the core power stripes to the ring. - ht.append( - f""" -# Core ring -add_rings -follow io -layer met5 -nets {{ {p_nets[0]} {g_nets[0]} }} -offset 40 -width 13 -spacing 3 -route_special -connect pad_pin -nets {{ {p_nets[0]} {g_nets[0]} }} -detailed_log - """ - ) - ht.append( - """ -# Prevent buffering on TIE_LO_ESD and TIE_HI_ESD -set_dont_touch [get_db [get_db pins -if {.name == *TIE*ESD}] .net] - """ - ) +# this needs to only be emitted in innovus, since it breaks the genus flow with the complaint that there are no usable inverters/logic cells (???) in version 211 +def set_cts_base_cells(ht: HammerTool) -> bool: + ht.append(""" +set_db cts_buffer_cells {CLKBUFX2 CLKBUFX4 CLKBUFX8} +set_db cts_clock_gating_cells {ICGX1} + """) return True +# TODO: this should just be placign rail straps, so higher straps are placed by non-hardocded tcl def power_rail_straps_no_tapcells(ht: HammerTool) -> bool: # We do this since there are no explicit tapcells in sky130_scl @@ -1122,6 +1067,45 @@ def power_rail_straps_no_tapcells(ht: HammerTool) -> bool: return True +# TODO do we want this to be in upstream hammer? +# def efabless_ring_io(ht: HammerTool) -> bool: +# assert isinstance(ht, HammerPlaceAndRouteTool), "IO ring instantiation only for par" +# assert isinstance(ht, TCLTool), "IO ring instantiation can only run on TCL tools" +# io_file = ht.get_setting("technology.sky130.io_file") +# ht.append(f"read_io_file {io_file} -no_die_size_adjust") +# p_nets = list(map(lambda s: s.name, ht.get_independent_power_nets())) +# g_nets = list(map(lambda s: s.name, ht.get_independent_ground_nets())) +# ht.append(f''' +# # Global net connections +# connect_global_net VDDA -type pg_pin -pin_base_name VDDA -verbose +# connect_global_net VDDIO -type pg_pin -pin_base_name VDDIO* -verbose +# connect_global_net {p_nets[0]} -type pg_pin -pin_base_name VCCD* -verbose +# connect_global_net {p_nets[0]} -type pg_pin -pin_base_name VCCHIB -verbose +# connect_global_net {p_nets[0]} -type pg_pin -pin_base_name VSWITCH -verbose +# connect_global_net {g_nets[0]} -type pg_pin -pin_base_name VSSA -verbose +# connect_global_net {g_nets[0]} -type pg_pin -pin_base_name VSSIO* -verbose +# connect_global_net {g_nets[0]} -type pg_pin -pin_base_name VSSD* -verbose +# ''') +# ht.append(''' +# # IO fillers +# set io_fillers {sky130_ef_io__com_bus_slice_20um sky130_ef_io__com_bus_slice_10um sky130_ef_io__com_bus_slice_5um sky130_ef_io__com_bus_slice_1um} +# add_io_fillers -io_ring 1 -cells $io_fillers -side top -filler_orient r0 +# add_io_fillers -io_ring 1 -cells $io_fillers -side right -filler_orient r270 +# add_io_fillers -io_ring 1 -cells $io_fillers -side bottom -filler_orient r180 +# add_io_fillers -io_ring 1 -cells $io_fillers -side left -filler_orient r90 +# ''') +# ht.append(f''' +# # Core ring +# add_rings -follow io -layer met5 -nets {{ {p_nets[0]} {g_nets[0]} }} -offset 5 -width 13 -spacing 3 +# route_special -connect pad_pin -nets {{ {p_nets[0]} {g_nets[0]} }} -detailed_log +# ''') +# ht.append(''' +# # Prevent buffering on TIE_LO_ESD and TIE_HI_ESD +# set_dont_touch [get_db [get_db pins -if {.name == *TIE*ESD}] .net] +# ''') +# return True + + def calibre_drc_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerDRCTool), "Exlude SRAMs only in DRC" drc_box = "" @@ -1133,6 +1117,24 @@ def calibre_drc_blackbox_srams(ht: HammerTool) -> bool: return True +# pegasus won't be able to drc the sky130a ios +def pegasus_drc_blackbox_io_cells(ht: HammerTool) -> bool: + assert ( + isinstance(ht, HammerDRCTool) and ht.tool_config_prefix() == "drc.pegasus" + ), "Exlude IOs only for Pegasus DRC" + drc_box = "" + io_cell_names = [ + "sky130_ef_io__*" + ] # TODO i don't think epgasus actually recognizes these? + # io_cell_names = ["sky130_ef_io__gpiov2_pad_wrapped"] + for name in io_cell_names: + drc_box += f"\nexclude_cell {name}" + run_file = ht.drc_ctl_file # type: ignore + with open(run_file, "a") as f: + f.write(drc_box) + return True + + def pegasus_drc_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerDRCTool), "Exlude SRAMs only in DRC" drc_box = "" @@ -1164,7 +1166,7 @@ def pegasus_lvs_add_130a_primitives(ht: HammerTool) -> bool: for name in SKY130Tech.sky130_sram_primitive_names(): lvs_box += f"""\nschematic_path "{name}" spice;""" # this is because otherwise lvs crashes with tons of stdcell-level pin mismatches - lvs_box += f"""\nlvs_inconsistent_reduction_threshold -none;""" + lvs_box += """\nlvs_inconsistent_reduction_threshold -none;""" run_file = ht.lvs_ctl_file # type: ignore with open(run_file, "r+") as f: # Remove SRAM SPICE file includes. @@ -1183,7 +1185,7 @@ def pegasus_lvs_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" lvs_box = "" for name in ( - SKY130Tech.sky130_sram_names() # + SKY130Tech.sky130_sram_primitive_names() + SKY130Tech.sky130_sram_names() # + SKY130Tech.sky130_sram_primitive_names() ): lvs_box += f"\nlvs_black_box {name} -gray" run_file = ht.lvs_ctl_file # type: ignore @@ -1210,21 +1212,6 @@ def sram22_lvs_recognize_gates_all(ht: HammerTool) -> bool: return True -LVS_DECK_SCRUB_LINES = [ - "VIRTUAL CONNECT REPORT", - "SOURCE PRIMARY", - "SOURCE SYSTEM SPICE", - "SOURCE PATH", - "ERC", - "LVS REPORT", -] - -LVS_DECK_INSERT_LINES = """ -LVS FILTER D OPEN SOURCE -LVS FILTER D OPEN LAYOUT -""" - - def setup_calibre_lvs_deck(ht: HammerTool) -> bool: assert isinstance(ht, HammerLVSTool), "Modify Calibre LVS deck for LVS only" # Remove conflicting specification statements found in PDK LVS decks diff --git a/hammer/technology/sky130/defaults.yml b/hammer/technology/sky130/defaults.yml index b22fb09e8..8e21cdbf4 100644 --- a/hammer/technology/sky130/defaults.yml +++ b/hammer/technology/sky130/defaults.yml @@ -1,4 +1,6 @@ # Settings for the sky130 technology +# + technology.sky130: sky130A: "/path/to/sky130A" # This should be output files from the OpenPDKs compilation process. # This is $PDK_ROOT/share/pdk/sky130A from the README. @@ -38,6 +40,10 @@ technology.sky130: stdcell_library: "sky130_fd_sc_hd" # Choose between "sky130_fd_sc_hd" (open-source) or "sky130_scl" (Cadence) +# WARNING: BREAK GLASS flag to override tech collateral with the paths provided in `override_libraries` +vlsi.technology.manually_override_pdk_collateral: false +vlsi.technology.override_libraries: [] # hacked collateral files to be used instead of default tech files with the same name + mentor.extra_env_vars_meta: lazydeepsubst # Mentor environment variables # Override this in project mentor.extra_env_vars: @@ -96,7 +102,7 @@ vlsi: tap_cell_interval: 15.0 # Set the interval and offset for tap cells tap_cell_offset: 5.0 - routing_layers: [2, 6] + routing_layers: [1, 6] technology.core: stackup: "sky130_fd_sc_hd" # This key should exist in the stackups list in the tech json diff --git a/hammer/technology/sky130/extra/efabless_template.io b/hammer/technology/sky130/extra/efabless_template.io index 524090739..cad54564d 100644 --- a/hammer/technology/sky130/extra/efabless_template.io +++ b/hammer/technology/sky130/extra/efabless_template.io @@ -65,8 +65,6 @@ (inst name = "" orientation=R270 offset=4392) (inst name = "clamp_6" orientation=R270 cell="sky130_ef_io__vccd_lvc_clamped3_pad" offset=4613) (inst name = "" orientation=R270 offset=4838) - (inst name = "IO_FILLER_MANUAL_E_1" orientation = R270 offset = 4593 cell = "sky130_ef_io__com_bus_slice_20um") - (inst name = "IO_FILLER_MANUAL_E_2" orientation = R270 offset = 2354 cell = "sky130_ef_io__com_bus_slice_20um") ) (bottomright @@ -117,8 +115,5 @@ (inst name = "clamp_16" orientation=R90 cell="sky130_ef_io__vddio_hvc_clamped_pad" offset=4349) (inst name = "clamp_17" orientation=R90 cell="sky130_ef_io__vccd_lvc_clamped3_pad" offset=4560) (inst name = "" orientation=R90 offset=4771) - (inst name = "IO_FILLER_MANUAL_W_1" orientation = R90 offset = 415 cell = "sky130_ef_io__com_bus_slice_20um") - (inst name = "IO_FILLER_MANUAL_W_2" orientation = R90 offset = 2279 cell = "sky130_ef_io__com_bus_slice_20um") - (inst name = "IO_FILLER_MANUAL_W_3" orientation = R90 offset = 4635 cell = "sky130_ef_io__com_bus_slice_20um") ) ) diff --git a/hammer/technology/sky130/extra/sram22/sram-cache-gen.py b/hammer/technology/sky130/extra/sram22/sram-cache-gen.py index 0b14da455..303724e14 100755 --- a/hammer/technology/sky130/extra/sram22/sram-cache-gen.py +++ b/hammer/technology/sky130/extra/sram22/sram-cache-gen.py @@ -10,25 +10,28 @@ import sys import re import json -import os from typing import List def main(args: List[str]) -> int: if len(args) != 3: - print("Usage: ./sram-cache-gen.py /path/to/sram22_sky130_macros output-file.json") - print("E.g.: ./sram-cache-gen.py /tools/C/me/sram22_sky130_macros sram-cache.json") + print("Usage: ./sram-cache-gen.py list-of-srams-1-per-line.txt output-file.json") + print("E.g.: ./sram-cache-gen.py srams.txt sram-cache.json") return 1 - list_of_srams: List[str] = [d.name for d in os.scandir(sys.argv[1]) if d.is_dir()] - print(f"Found {len(list_of_srams)} SRAMS to cache") + list_of_srams = [] # type: List[str] + with open(sys.argv[1]) as f: + for line in f: + list_of_srams.append(line) + + print(str(len(list_of_srams)) + " SRAMs to cache") sram_dicts = [] for sram_name in list_of_srams: # SRAM22-generated single-port RAMs if sram_name.startswith("sram22_"): - match = re.match(r"^sram22_(\d+)x(\d+)m(\d+)w(\d+)$", sram_name) + match = re.match(r"sram22_(\d+)x(\d+)m(\d+)w(\d+)(\D*)", sram_name) if match: width = int(match.group(2)) mask_gran = int(match.group(4)) @@ -52,8 +55,11 @@ def main(args: List[str]) -> int: port_dict['clock port name'] = "clk" port_dict['clock port polarity'] = "active high" + port_dict['chip enable port name'] = "ce" + port_dict['chip enable port polarity'] = "active high" # ??? + port_dict['write enable port name'] = "we" - port_dict['write enable port polarity'] = "active high" + port_dict['write enable port polarity'] = "active high" # ??? port_dict['output port name'] = "dout" port_dict['output port polarity'] = "active high" @@ -61,17 +67,27 @@ def main(args: List[str]) -> int: port_dict['input port name'] = "din" port_dict['input port polarity'] = "active high" - # if mask_gran != width: port_dict['mask port name'] = "wmask" port_dict['mask granularity'] = mask_gran port_dict['mask port polarity'] = "active high" # ??? sram_dict['ports'].append(port_dict.copy()) + sram_dict['extra ports'] = [{ + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1, + }] + sram_dicts.append(sram_dict.copy()) else: - print("Skipping unsupported memory: {n}".format(n=sram_name), file=sys.stderr) + print("Unsupported memory: {n}".format(n=sram_name), file=sys.stderr) + return 1 + else: + print("Unsupported memory: {n}".format(n=sram_name), file=sys.stderr) + return 1 with open(sys.argv[2], "w") as f: json.dump(sram_dicts, f, indent=2) diff --git a/hammer/technology/sky130/extra/sram22/sram-cache.json b/hammer/technology/sky130/extra/sram22/sram-cache.json index 045da80ef..fdad68e08 100644 --- a/hammer/technology/sky130/extra/sram22/sram-cache.json +++ b/hammer/technology/sky130/extra/sram22/sram-cache.json @@ -1,10 +1,10 @@ [ { "type": "sram", - "name": "sram22_512x32m4w32", + "name": "sram22_64x24m4w8", "source": "sram22", - "depth": "512", - "width": 32, + "depth": "64", + "width": 24, "family": "1rw", "mask": "true", "vt": "svt", @@ -15,6 +15,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -22,14 +24,22 @@ "input port name": "din", "input port polarity": "active high", "mask port name": "wmask", - "mask granularity": 32, + "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_64x32m4w32", + "name": "sram22_64x32m4w8", "source": "sram22", "depth": "64", "width": 32, @@ -43,6 +53,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -50,17 +62,25 @@ "input port name": "din", "input port polarity": "active high", "mask port name": "wmask", - "mask granularity": 32, + "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_512x64m4w8", + "name": "sram22_128x16m4w8", "source": "sram22", - "depth": "512", - "width": 64, + "depth": "128", + "width": 16, "family": "1rw", "mask": "true", "vt": "svt", @@ -71,6 +91,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -81,14 +103,22 @@ "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_512x32m4w8", + "name": "sram22_128x24m4w8", "source": "sram22", - "depth": "512", - "width": 32, + "depth": "128", + "width": 24, "family": "1rw", "mask": "true", "vt": "svt", @@ -99,6 +129,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -109,14 +141,22 @@ "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_64x24m4w24", + "name": "sram22_128x32m4w8", "source": "sram22", - "depth": "64", - "width": 24, + "depth": "128", + "width": 32, "family": "1rw", "mask": "true", "vt": "svt", @@ -127,6 +167,84 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 8, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_256x8m8w1", + "source": "sram22", + "depth": "256", + "width": 8, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 8, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 1, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_256x16m8w8", + "source": "sram22", + "depth": "256", + "width": 16, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 8, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -134,9 +252,17 @@ "input port name": "din", "input port polarity": "active high", "mask port name": "wmask", - "mask granularity": 24, + "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { @@ -155,6 +281,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -165,14 +293,22 @@ "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_64x4m4w2", + "name": "sram22_256x64m4w8", "source": "sram22", - "depth": "64", - "width": 4, + "depth": "256", + "width": 64, "family": "1rw", "mask": "true", "vt": "svt", @@ -183,6 +319,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -190,17 +328,63 @@ "input port name": "din", "input port polarity": "active high", "mask port name": "wmask", - "mask granularity": 2, + "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_2048x32m8w8", + "name": "sram22_256x128m4w8", "source": "sram22", - "depth": "2048", - "width": 32, + "depth": "256", + "width": 128, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 4, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 8, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_512x8m8w1", + "source": "sram22", + "depth": "512", + "width": 8, "family": "1rw", "mask": "true", "vt": "svt", @@ -211,6 +395,46 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 1, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_512x32m4w8", + "source": "sram22", + "depth": "512", + "width": 32, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 4, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -221,14 +445,98 @@ "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_1024x32m8w32", + "name": "sram22_512x64m4w8", + "source": "sram22", + "depth": "512", + "width": 64, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 4, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 8, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_512x128m4w8", + "source": "sram22", + "depth": "512", + "width": 128, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 4, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 8, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_1024x8m8w1", "source": "sram22", "depth": "1024", - "width": 32, + "width": 8, "family": "1rw", "mask": "true", "vt": "svt", @@ -239,6 +547,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -246,9 +556,17 @@ "input port name": "din", "input port polarity": "active high", "mask port name": "wmask", - "mask granularity": 32, + "mask granularity": 1, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { @@ -267,6 +585,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -277,14 +597,22 @@ "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_64x32m4w8", + "name": "sram22_1024x64m4w8", "source": "sram22", - "depth": "64", - "width": 32, + "depth": "1024", + "width": 64, "family": "1rw", "mask": "true", "vt": "svt", @@ -295,6 +623,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -305,6 +635,52 @@ "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] - } + }, + { + "type": "sram", + "name": "sram22_2048x8m8w1", + "source": "sram22", + "depth": "2048", + "width": 8, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 8, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 1, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, ] diff --git a/hammer/technology/sky130/extra/sram22/srams.txt b/hammer/technology/sky130/extra/sram22/srams.txt new file mode 100644 index 000000000..94a6c1125 --- /dev/null +++ b/hammer/technology/sky130/extra/sram22/srams.txt @@ -0,0 +1,22 @@ +sram22_64x24m4w8 +sram22_64x32m4w8 +sram22_128x16m4w8 +sram22_128x24m4w8 +sram22_128x32m4w8 +sram22_256x8m8w1 +sram22_256x16m8w8 +sram22_256x32m4w8 +sram22_256x64m4w8 +sram22_256x128m4w8 +sram22_512x8m8w1 +sram22_512x32m4w8 +sram22_512x64m4w8 +sram22_512x128m4w8 +sram22_1024x8m8w1 +sram22_1024x32m8w8 +sram22_1024x64m4w8 +sram22_2048x8m8w1 +sram22_2048x32m8w8 +sram22_4096x8m8w1 +sram22_4096x32m8w8 +sram22_8192x32m8w8 diff --git a/hammer/technology/sky130/sky130_lefpin.map b/hammer/technology/sky130/sky130_lefpin.map index 8d84b6219..9081d6ca4 100644 --- a/hammer/technology/sky130/sky130_lefpin.map +++ b/hammer/technology/sky130/sky130_lefpin.map @@ -44,4 +44,5 @@ met5 LEFPIN,LEFOBS,PIN,NET,SPNET,VIA 72 20 met5 PIN 72 16 NAME met5/PIN,met5/LEFPIN 72 5 -DIEAREA ALL 235 4 \ No newline at end of file +AREAIDLD ALL 81 14 +DIEAREA ALL 235 4 diff --git a/hammer/technology/sky130/sram-cache.json b/hammer/technology/sky130/sram-cache.json index 3683de4ed..fdad68e08 100644 --- a/hammer/technology/sky130/sram-cache.json +++ b/hammer/technology/sky130/sram-cache.json @@ -1,10 +1,10 @@ [ { "type": "sram", - "name": "sram22_512x32m4w32", + "name": "sram22_64x24m4w8", "source": "sram22", - "depth": "512", - "width": 32, + "depth": "64", + "width": 24, "family": "1rw", "mask": "true", "vt": "svt", @@ -15,6 +15,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -22,14 +24,22 @@ "input port name": "din", "input port polarity": "active high", "mask port name": "wmask", - "mask granularity": 32, + "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_64x32m4w32", + "name": "sram22_64x32m4w8", "source": "sram22", "depth": "64", "width": 32, @@ -43,6 +53,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -50,17 +62,25 @@ "input port name": "din", "input port polarity": "active high", "mask port name": "wmask", - "mask granularity": 32, + "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_512x64m4w8", + "name": "sram22_128x16m4w8", "source": "sram22", - "depth": "512", - "width": 64, + "depth": "128", + "width": 16, "family": "1rw", "mask": "true", "vt": "svt", @@ -71,6 +91,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -81,14 +103,22 @@ "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_512x32m4w8", + "name": "sram22_128x24m4w8", "source": "sram22", - "depth": "512", - "width": 32, + "depth": "128", + "width": 24, "family": "1rw", "mask": "true", "vt": "svt", @@ -99,6 +129,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -109,14 +141,22 @@ "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_64x24m4w24", + "name": "sram22_128x32m4w8", "source": "sram22", - "depth": "64", - "width": 24, + "depth": "128", + "width": 32, "family": "1rw", "mask": "true", "vt": "svt", @@ -127,6 +167,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -134,9 +176,93 @@ "input port name": "din", "input port polarity": "active high", "mask port name": "wmask", - "mask granularity": 24, + "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_256x8m8w1", + "source": "sram22", + "depth": "256", + "width": 8, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 8, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 1, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_256x16m8w8", + "source": "sram22", + "depth": "256", + "width": 16, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 8, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 8, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { @@ -155,6 +281,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -165,14 +293,22 @@ "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_64x4m4w2", + "name": "sram22_256x64m4w8", "source": "sram22", - "depth": "64", - "width": 4, + "depth": "256", + "width": 64, "family": "1rw", "mask": "true", "vt": "svt", @@ -183,6 +319,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -190,20 +328,218 @@ "input port name": "din", "input port polarity": "active high", "mask port name": "wmask", - "mask granularity": 2, + "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_1024x32m8w32", + "name": "sram22_256x128m4w8", "source": "sram22", - "depth": "1024", + "depth": "256", + "width": 128, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 4, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 8, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_512x8m8w1", + "source": "sram22", + "depth": "512", + "width": 8, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 8, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 1, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_512x32m4w8", + "source": "sram22", + "depth": "512", "width": 32, "family": "1rw", "mask": "true", "vt": "svt", + "mux": 4, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 8, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_512x64m4w8", + "source": "sram22", + "depth": "512", + "width": 64, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 4, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 8, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_512x128m4w8", + "source": "sram22", + "depth": "512", + "width": 128, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 4, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 8, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_1024x8m8w1", + "source": "sram22", + "depth": "1024", + "width": 8, + "family": "1rw", + "mask": "true", + "vt": "svt", "mux": 8, "ports": [ { @@ -211,6 +547,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -218,9 +556,17 @@ "input port name": "din", "input port polarity": "active high", "mask port name": "wmask", - "mask granularity": 32, + "mask granularity": 1, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { @@ -239,6 +585,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -249,14 +597,22 @@ "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] }, { "type": "sram", - "name": "sram22_64x32m4w8", + "name": "sram22_1024x64m4w8", "source": "sram22", - "depth": "64", - "width": 32, + "depth": "1024", + "width": 64, "family": "1rw", "mask": "true", "vt": "svt", @@ -267,6 +623,8 @@ "address port polarity": "active high", "clock port name": "clk", "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", "write enable port name": "we", "write enable port polarity": "active high", "output port name": "dout", @@ -277,6 +635,52 @@ "mask granularity": 8, "mask port polarity": "active high" } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } + ] + }, + { + "type": "sram", + "name": "sram22_2048x8m8w1", + "source": "sram22", + "depth": "2048", + "width": 8, + "family": "1rw", + "mask": "true", + "vt": "svt", + "mux": 8, + "ports": [ + { + "address port name": "addr", + "address port polarity": "active high", + "clock port name": "clk", + "clock port polarity": "active high", + "chip enable port name": "ce", + "chip enable port polarity": "active high", + "write enable port name": "we", + "write enable port polarity": "active high", + "output port name": "dout", + "output port polarity": "active high", + "input port name": "din", + "input port polarity": "active high", + "mask port name": "wmask", + "mask granularity": 1, + "mask port polarity": "active high" + } + ], + "extra ports": [ + { + "name": "rstb", + "width": 1, + "type": "constant", + "value": 1 + } ] - } + }, ] diff --git a/hammer/technology/sky130/sram_compiler/__init__.py b/hammer/technology/sky130/sram_compiler/__init__.py index e710304e1..bcd57ae5a 100644 --- a/hammer/technology/sky130/sram_compiler/__init__.py +++ b/hammer/technology/sky130/sram_compiler/__init__.py @@ -37,6 +37,7 @@ 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) # type: ignore + # SRAM22 SRAMs if params.name.startswith("sram22"): self.logger.info(f"Compiling {params.family} memories to SRAM22 instances") # s=round(round(params.width*params.depth/8, -3)/1000) # size in kiB @@ -45,38 +46,33 @@ def generate_sram(self, params: SRAMParameters, corner: MMMCCorner) -> ExtraLibr sram_name = params.name #TODO: replace this if SRAM22 characterization done for other corners # we only have typical lib for sky130 srams - corner_str = "tt_025C_1v80" - #corner_str = "{speed}_{volt}V_{temp}C".format( - # speed = speed, - # volt = str(corner.voltage.value_in_units("V")).replace(".","p"), - # temp = str(int(corner.temp.value_in_units("C"))).replace(".","p")) + temp = corner.temp.value_in_units("C") + corner_str = "{speed}_{temp}C_{volt}".format( + speed = speed.lower(), + volt = "{:.2f}".format(corner.voltage.value_in_units("V")).replace(".","v"), + temp = ("{:03g}".format(temp).replace(".","p") if temp > 0 else "n{:02g}".format(-temp).replace(".","p")) + ) base_dir=self.get_setting('technology.sky130.sram22_sky130_macros') - - found = False - for fidelity in ["rcc", "rc", "c"]: - lib_path="{b}/{n}/{n}_{c}.{f}.lib".format(b=base_dir,n=sram_name,c=corner_str, f=fidelity) - if os.path.exists(lib_path): - found = True - break - else: - self.logger.warning(f"SKY130 {params.name} SRAM cache does not support corner {corner_str} with {fidelity} extraction") - - if not found: - self.logger.error(f"SKY130 {params.name} SRAM cache does not support corner {corner_str}") - + lib_path="{b}/{n}/{n}_{c}.lib".format(b=base_dir,n=sram_name,c=corner_str) + if not os.path.exists(lib_path): + self.logger.error(f"SKY130 {params.family} SRAM cache does not support corner: {corner_str}") + + lef_file="{b}/{n}/{n}.lef".format(b=base_dir,n=sram_name) + if not os.path.exists(lef_file): + self.logger.error(f"No LEF for: {sram_name} ({lef_file})") + 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), gds_file="{b}/{n}/{n}.gds".format(b=base_dir,n=sram_name), - spice_file="{b}/{n}/{n}.spice".format(b=base_dir,n=sram_name), verilog_sim="{b}/{n}/{n}.v".format(b=base_dir,n=sram_name), corner=Corner(nmos=speed_name, pmos=speed_name, temperature=str(corner.temp.value_in_units("C")) + " C"), supplies=Supplies(VDD=str(corner.voltage.value_in_units("V")) + " V", GND="0 V"), provides=[Provide(lib_type="sram", vt=params.vt)])) - # TODO: remove OpenRAM support very soon + # OpenRAM SRAMs elif params.name.startswith("sky130_sram_"): self.logger.info(f"Compiling {params.family} memories to OpenRAM instances") base_dir = self.get_setting("technology.sky130.openram_lib") diff --git a/hammer/vlsi/driver.py b/hammer/vlsi/driver.py index 9a97c53da..7d24348dc 100644 --- a/hammer/vlsi/driver.py +++ b/hammer/vlsi/driver.py @@ -162,6 +162,7 @@ def load_technology(self, cache_dir: str = "") -> None: self.log.fatal("Technology {0} config not generated or missing .tech.[json/yml]!".format(tech_module)) return tech.extract_technology_files() + tech.override_tech_libraries() tech.get_lib_units() self.tech = tech From 889dae6904d94685631a4b2101ae2392118cca12 Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 22 Dec 2024 20:23:38 -0800 Subject: [PATCH 36/68] fix manual netlist override parsing logic --- hammer/tech/__init__.py | 16 ++++++++++++---- hammer/technology/sky130/__init__.py | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/hammer/tech/__init__.py b/hammer/tech/__init__.py index 768365a0b..347e795de 100644 --- a/hammer/tech/__init__.py +++ b/hammer/tech/__init__.py @@ -345,7 +345,6 @@ def override_tech_libraries(self) -> None: if len(self.get_setting("vlsi.technology.override_libraries")) > 0: self.logger.warning("You've attempted to specify override libraries without enabling vlsi.technology.manually_override_pdk_collateral! collateral paths will not be overwritten") - used_overrides = [] for lib in self.config.libraries: for field_name in lib.model_fields: @@ -362,16 +361,25 @@ def override_tech_libraries(self) -> None: cached_paths = [f for f in cached_paths if os.path.exists(f)] # only allow 1 valid cached path, otherwise ambiguous - assert len(cached_paths) <= 1, f"ambiguous cache override: {cached_paths}" + if len(cached_paths) > 1: + self.logger.error(f"ambiguous cache override: {cached_paths}") if cached_paths: + used_overrides.append((field_name, fname)) new_path = cached_paths[0] self.logger.info( f"overriding {default_path} with cache entry {new_path}" ) + + manual_override_paths = set([f for f in fnames if (field_name, f) in manual_overrides]) + manual_override_paths = [manual_overrides[(field_name, f)] for f in manual_override_paths] + + # only allow 1 valid manual_override path, otherwise ambiguous + if len(manual_override_paths) > 1: + self.logger.error(f"ambiguous cache override: {manual_override_paths}") # prioritize manual overrides over cache - if (field_name, fname) in manual_overrides: + if manual_override_paths: used_overrides.append((field_name, fname)) - new_path = manual_overrides[(field_name, fname)] + new_path = manual_override_paths[0] self.logger.info( f"overriding {default_path} with manual override {new_path}" ) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 55a76bd60..a05755615 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -1208,7 +1208,7 @@ def sram22_lvs_recognize_gates_all(ht: HammerTool) -> bool: ), "Change 'LVS RECOGNIZE GATES' from 'NONE' to 'ALL' for SRAM22" run_file = ht.lvs_run_file # type: ignore with open(run_file, "a") as f: - f.write("LVS RECOGNIZE GATES ALL") + f.write("\nLVS RECOGNIZE GATES ALL") return True From 89cec061923c22d6f2bda39a8f18bd4111d0f41c Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 22 Dec 2024 22:48:19 -0800 Subject: [PATCH 37/68] try make e2e work --- .github/workflows/e2e-cad.yml | 44 +++++++++++++++++++++++++++++++++++ e2e/Makefile | 2 +- e2e/configs-env/bwrc-env.yml | 26 +++++++++++++++------ e2e/configs-tool/cm.yml | 4 ++-- e2e/pyproject.toml | 2 +- 5 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/e2e-cad.yml diff --git a/.github/workflows/e2e-cad.yml b/.github/workflows/e2e-cad.yml new file mode 100644 index 000000000..4eea3b99c --- /dev/null +++ b/.github/workflows/e2e-cad.yml @@ -0,0 +1,44 @@ +name: End-to-end CAD flow CI smoketest + +on: + push: + branches: + - main + - test-ci + pull_request: + +jobs: + test: + runs-on: [self-hosted] # TODO: replace with something centrally admined + + steps: + # Step 1: Create a temporary directory + - name: Create temporary directory + run: | + TMP_DIR=$(mktemp -d) + echo "Temporary directory created at: $TMP_DIR" + echo "TMP_DIR=$TMP_DIR" >> $GITHUB_ENV + + # Clone this repo + - name: Checkout + uses: actions/checkout@v4.1.6 + + + - name: Run `make build` in the `e2e` directory + run: | + cd e2e + # TODO make sure poetry is installed + #curl -sSL https://install.python-poetry.org | python3 - + poetry install + poetry shell + # redirect all hammer output a temp file, since it contains proprietary tool logs + tempfile=$(mktemp) + echo "log file is $tempfile on $(uname -a)" | tee -a $tempfile + echo "End-to-end CAD flow CI smoketest running on" ${{ github.head_ref }}.${{ github.sha }} > $tempfile 2>&1 + make build >> $tempfile 2>&1 + echo "running par.." | tee -a $tempfile + make par >> $tempfile 2>&1 + echo "running drc.." | tee -a $tempfile + make drc >> $tempfile 2>&1 + echo "running lvs.." | tee -a $tempfile + make lvs >> $tempfile 2>&1 diff --git a/e2e/Makefile b/e2e/Makefile index a66bb878c..649e44387 100644 --- a/e2e/Makefile +++ b/e2e/Makefile @@ -5,7 +5,7 @@ vlsi_dir=$(abspath .) # minimal flow configuration variables design ?= pass pdk ?= sky130 -tools ?= nop +tools ?= cm env ?= bwrc extra ?= # extra configs diff --git a/e2e/configs-env/bwrc-env.yml b/e2e/configs-env/bwrc-env.yml index e4fc2429e..7b0162ce2 100644 --- a/e2e/configs-env/bwrc-env.yml +++ b/e2e/configs-env/bwrc-env.yml @@ -27,14 +27,26 @@ drc.magic.magic_bin: "/tools/commercial/skywater/local/chipyard-tutorial/.conda- lvs.netgen.netgen_bin: "/tools/commercial/skywater/local/chipyard-tutorial/.conda-signoff/bin/netgen" -# Sky130 paths +# Technology paths technology.sky130: - sky130A: "/tools/commercial/skywater/local/sky130A" - sram22_sky130_macros: "/tools/commercial/skywater/local/sram22_sky130_macros" - openram_lib: "/tools/commercial/skywater/local/sky130_sram_macros" - sky130_nda: "/tools/commercial/skywater/swtech130/skywater-src-nda-20221031" - sky130_cds: "/tools/commercial/skywater/sky130_cds/PDK/sky130_release_0.0.3/" - sky130_scl: "/tools/commercial/skywater/sky130_cds/LIB/sky130_scl_9T_0.0.3/" + sky130A: "/tools/commercial/skywater/local/open_pdks-2022.10/share/pdk/sky130A" + # sram22_sky130_macros: "/tools/commercial/skywater/local/chipyard-tutorial/sram22_sky130_macros" + sram22_sky130_macros: "/tools/C/rohankumar/stac-top/vlsi/sram22_sky130_macros/" + # https://github.com/rahulk29/sram22_sky130_macros/tree/dev + + # this key is OPTIONAL, no NDA files will be used if it does not point to a valid path + sky130_nda: "/tools/commercial/skywater/swtech130/skywater-src-nda" + + # for caravel collateral pulled in by this design + caravel: /tools/commercial/skywater/local/caravel/v6.0 + + lvs_blackbox_srams: true + sky130_scl: "/tools/C/elamdf/sky130/sky130_scl_9T_0.0.6" + sky130_cds: "/tools/C/elamdf/sky130/sky130_release_0.0.4/" + stdcell_library: "sky130_scl" + +technology.core.stackup: "sky130_scl" +vlsi.technology.placement_site: "CoreSite" # ASAP7 paths technology.asap7: diff --git a/e2e/configs-tool/cm.yml b/e2e/configs-tool/cm.yml index df7785586..a1e35bf3c 100644 --- a/e2e/configs-tool/cm.yml +++ b/e2e/configs-tool/cm.yml @@ -9,5 +9,5 @@ vlsi.core.sim_tool: "hammer.sim.vcs" vlsi.core.timing_tool: "hammer.timing.tempus" vlsi.core.formal_tool: "hammer.formal.conformal" vlsi.core.power_tool: "hammer.power.voltus" -vlsi.core.drc_tool: "hammer.drc.calibre" -vlsi.core.lvs_tool: "hammer.lvs.calibre" +vlsi.core.drc_tool: "hammer.drc.pegasus" +vlsi.core.lvs_tool: "hammer.lvs.pegasus" diff --git a/e2e/pyproject.toml b/e2e/pyproject.toml index a63016fb8..23c059a6f 100644 --- a/e2e/pyproject.toml +++ b/e2e/pyproject.toml @@ -10,7 +10,7 @@ repository = "https://github.com/ucb-bar/hammer" [tool.poetry.dependencies] python = "^3.9" hammer-vlsi = {path = "../.", extras = ["asap7"], develop = true} -hammer-mentor-plugins = {path = "hammer-mentor-plugins", develop = true} +#hammer-mentor-plugins = {path = "hammer-mentor-plugins", develop = true} [build-system] requires = ["poetry-core>=1.0.8", "setuptools>=65.3"] From 7f0f61c360d8469386400e95b59694d3526143dc Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 22 Dec 2024 22:49:04 -0800 Subject: [PATCH 38/68] add sky130_pydantic --- .github/workflows/e2e-cad.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/e2e-cad.yml b/.github/workflows/e2e-cad.yml index 4eea3b99c..4be3398c9 100644 --- a/.github/workflows/e2e-cad.yml +++ b/.github/workflows/e2e-cad.yml @@ -5,6 +5,7 @@ on: branches: - main - test-ci + - sky130_pydantic pull_request: jobs: From b79fce7259035941686ba6a261b1fd1ce344bb1d Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 22 Dec 2024 22:51:34 -0800 Subject: [PATCH 39/68] remove NDA libs from ci config and allow sky130a to not be set --- e2e/configs-env/bwrc-env.yml | 4 ++-- hammer/technology/sky130/__init__.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/e2e/configs-env/bwrc-env.yml b/e2e/configs-env/bwrc-env.yml index 7b0162ce2..1bc3686f8 100644 --- a/e2e/configs-env/bwrc-env.yml +++ b/e2e/configs-env/bwrc-env.yml @@ -29,13 +29,13 @@ lvs.netgen.netgen_bin: "/tools/commercial/skywater/local/chipyard-tutorial/.cond # Technology paths technology.sky130: - sky130A: "/tools/commercial/skywater/local/open_pdks-2022.10/share/pdk/sky130A" + #sky130A: "/tools/commercial/skywater/local/open_pdks-2022.10/share/pdk/sky130A" # sram22_sky130_macros: "/tools/commercial/skywater/local/chipyard-tutorial/sram22_sky130_macros" sram22_sky130_macros: "/tools/C/rohankumar/stac-top/vlsi/sram22_sky130_macros/" # https://github.com/rahulk29/sram22_sky130_macros/tree/dev # this key is OPTIONAL, no NDA files will be used if it does not point to a valid path - sky130_nda: "/tools/commercial/skywater/swtech130/skywater-src-nda" + #sky130_nda: "/tools/commercial/skywater/swtech130/skywater-src-nda" # for caravel collateral pulled in by this design caravel: /tools/commercial/skywater/local/caravel/v6.0 diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index a05755615..1524ee71c 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -297,9 +297,10 @@ def gen_config(self) -> None: # add skywater io cells io_library_base_path = os.path.join(SKY130A, "libs.ref", "sky130_fd_io") - lib_corner_files[io_library_base_path] = os.listdir( - os.path.join(io_library_base_path, "lib") - ) + if os.path.exists(io_library_base_path): + lib_corner_files[io_library_base_path] = os.listdir( + os.path.join(io_library_base_path, "lib") + ) for library_base_path, cornerfiles in lib_corner_files.items(): for cornerfilename in cornerfiles: From 7fe5b419f050fe4502f3d8b5e194b70581f50d7c Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 22 Dec 2024 22:53:01 -0800 Subject: [PATCH 40/68] use hostname instead of uname -a --- .github/workflows/e2e-cad.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-cad.yml b/.github/workflows/e2e-cad.yml index 4be3398c9..1bb53a262 100644 --- a/.github/workflows/e2e-cad.yml +++ b/.github/workflows/e2e-cad.yml @@ -34,7 +34,7 @@ jobs: poetry shell # redirect all hammer output a temp file, since it contains proprietary tool logs tempfile=$(mktemp) - echo "log file is $tempfile on $(uname -a)" | tee -a $tempfile + echo "log file is $tempfile on $(hostname)" | tee -a $tempfile echo "End-to-end CAD flow CI smoketest running on" ${{ github.head_ref }}.${{ github.sha }} > $tempfile 2>&1 make build >> $tempfile 2>&1 echo "running par.." | tee -a $tempfile From 1328c820f1ced711495fdbd2692878f49cb578cf Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 22 Dec 2024 22:54:05 -0800 Subject: [PATCH 41/68] rename test --- .github/workflows/e2e-cad.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e-cad.yml b/.github/workflows/e2e-cad.yml index 1bb53a262..04f879f32 100644 --- a/.github/workflows/e2e-cad.yml +++ b/.github/workflows/e2e-cad.yml @@ -1,4 +1,4 @@ -name: End-to-end CAD flow CI smoketest +name: End-to-end CAD flow smoketests on: push: @@ -9,7 +9,7 @@ on: pull_request: jobs: - test: + sky130-cm-e2e: runs-on: [self-hosted] # TODO: replace with something centrally admined steps: From 2c77e957f272eb2ebce0ea741ad6adade47347c9 Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 22 Dec 2024 22:55:01 -0800 Subject: [PATCH 42/68] remove trailing comma that breaks parsing in sram cache --- hammer/technology/sky130/sram-cache.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hammer/technology/sky130/sram-cache.json b/hammer/technology/sky130/sram-cache.json index fdad68e08..150eb9f0d 100644 --- a/hammer/technology/sky130/sram-cache.json +++ b/hammer/technology/sky130/sram-cache.json @@ -682,5 +682,5 @@ "value": 1 } ] - }, + } ] From 6873b9c8dcf6e1508fa820c280b97b6e8f99e23e Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 22 Dec 2024 22:59:07 -0800 Subject: [PATCH 43/68] fix poetry env usage --- .github/workflows/e2e-cad.yml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/.github/workflows/e2e-cad.yml b/.github/workflows/e2e-cad.yml index 04f879f32..fd9db7e66 100644 --- a/.github/workflows/e2e-cad.yml +++ b/.github/workflows/e2e-cad.yml @@ -13,25 +13,15 @@ jobs: runs-on: [self-hosted] # TODO: replace with something centrally admined steps: - # Step 1: Create a temporary directory - - name: Create temporary directory - run: | - TMP_DIR=$(mktemp -d) - echo "Temporary directory created at: $TMP_DIR" - echo "TMP_DIR=$TMP_DIR" >> $GITHUB_ENV - # Clone this repo - name: Checkout uses: actions/checkout@v4.1.6 - - - name: Run `make build` in the `e2e` directory + - name: Run syn (Genus), par (Innovus), drc (Pegasus), and lvs (Pegasus) on the GCD design in sky130, using sky130_scl. run: | cd e2e - # TODO make sure poetry is installed - #curl -sSL https://install.python-poetry.org | python3 - poetry install - poetry shell + source $(poetry env info --path)/bin/activate # redirect all hammer output a temp file, since it contains proprietary tool logs tempfile=$(mktemp) echo "log file is $tempfile on $(hostname)" | tee -a $tempfile From c189a4907168c7433ab70b1631679a3509465772 Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 22 Dec 2024 23:06:51 -0800 Subject: [PATCH 44/68] check lvs and drc passing --- .github/workflows/e2e-cad.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/e2e-cad.yml b/.github/workflows/e2e-cad.yml index fd9db7e66..ec2bedf3b 100644 --- a/.github/workflows/e2e-cad.yml +++ b/.github/workflows/e2e-cad.yml @@ -33,3 +33,20 @@ jobs: make drc >> $tempfile 2>&1 echo "running lvs.." | tee -a $tempfile make lvs >> $tempfile 2>&1 + + # Verify DRC/LVS is clean (this is a very simple design, it should be clean without hacks) + + echo "Checking that LVS passed..." | tee -a $tempfile + # TODO these are probably pretty brittle... + if grep -q "Result : MATCH" "$tempfile"; then + echo "There's an LVS mismatch! see $tempfile for log" | tee -a $tempfile + exit 1 + fi + echo "LVS passed!" | tee -a $tempfile + + echo "Checking that DRC is clean..." | tee -a $tempfile + if grep -q "Total DRC Results : 0 (0)" "$tempfile"; then + echo "There are DRC violations! see $tempfile for log" | tee -a $tempfile + exit 1 + fi + echo "DRC is clean!" | tee -a $tempfile From 66bda46e34dde27cc68bc3769c2c4b79cd7a6fb5 Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 22 Dec 2024 23:19:53 -0800 Subject: [PATCH 45/68] fix log checking --- .github/workflows/e2e-cad.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e-cad.yml b/.github/workflows/e2e-cad.yml index ec2bedf3b..b7615139f 100644 --- a/.github/workflows/e2e-cad.yml +++ b/.github/workflows/e2e-cad.yml @@ -38,14 +38,14 @@ jobs: echo "Checking that LVS passed..." | tee -a $tempfile # TODO these are probably pretty brittle... - if grep -q "Result : MATCH" "$tempfile"; then + if grep "Run Result : MATCH" "$tempfile"; then echo "There's an LVS mismatch! see $tempfile for log" | tee -a $tempfile exit 1 fi echo "LVS passed!" | tee -a $tempfile echo "Checking that DRC is clean..." | tee -a $tempfile - if grep -q "Total DRC Results : 0 (0)" "$tempfile"; then + if grep "Total DRC Results : 0 (0)" "$tempfile"; then echo "There are DRC violations! see $tempfile for log" | tee -a $tempfile exit 1 fi From 675e7edbe93268b15813b9bf6f240ca8578d98de Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 22 Dec 2024 23:27:30 -0800 Subject: [PATCH 46/68] I don't know how to use bash --- .github/workflows/e2e-cad.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e-cad.yml b/.github/workflows/e2e-cad.yml index b7615139f..cfe02e39e 100644 --- a/.github/workflows/e2e-cad.yml +++ b/.github/workflows/e2e-cad.yml @@ -38,14 +38,14 @@ jobs: echo "Checking that LVS passed..." | tee -a $tempfile # TODO these are probably pretty brittle... - if grep "Run Result : MATCH" "$tempfile"; then + if [[ $(grep "Run Result : MATCH" "$tempfile") ]]; then echo "There's an LVS mismatch! see $tempfile for log" | tee -a $tempfile exit 1 fi echo "LVS passed!" | tee -a $tempfile echo "Checking that DRC is clean..." | tee -a $tempfile - if grep "Total DRC Results : 0 (0)" "$tempfile"; then + if [[ $(grep "Total DRC Results : 0 (0)" "$tempfile") ]]; then echo "There are DRC violations! see $tempfile for log" | tee -a $tempfile exit 1 fi From ac9191beb2dfd00bdeedd387341bd273179e366b Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 22 Dec 2024 23:36:05 -0800 Subject: [PATCH 47/68] I don't know how to use conditional logic --- .github/workflows/e2e-cad.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e-cad.yml b/.github/workflows/e2e-cad.yml index cfe02e39e..4402cc5f9 100644 --- a/.github/workflows/e2e-cad.yml +++ b/.github/workflows/e2e-cad.yml @@ -39,14 +39,16 @@ jobs: echo "Checking that LVS passed..." | tee -a $tempfile # TODO these are probably pretty brittle... if [[ $(grep "Run Result : MATCH" "$tempfile") ]]; then + echo "LVS passed!" | tee -a $tempfile + else echo "There's an LVS mismatch! see $tempfile for log" | tee -a $tempfile exit 1 fi - echo "LVS passed!" | tee -a $tempfile echo "Checking that DRC is clean..." | tee -a $tempfile if [[ $(grep "Total DRC Results : 0 (0)" "$tempfile") ]]; then + echo "DRC is clean!" | tee -a $tempfile + else echo "There are DRC violations! see $tempfile for log" | tee -a $tempfile exit 1 fi - echo "DRC is clean!" | tee -a $tempfile From ac5887f872c0f3781b896068298f6e8153cdce34 Mon Sep 17 00:00:00 2001 From: elamdf Date: Mon, 23 Dec 2024 00:15:45 -0800 Subject: [PATCH 48/68] fix pegasus lvs on sky130_fd_sc_hd --- hammer/technology/sky130/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 1524ee71c..89f944dbd 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -497,6 +497,9 @@ def setup_cdl(self) -> None: if self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.calibre": pmos = "phighvt" nmos = "nshort" + elif self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.pegasus": + pmos = "pfet_01v8_hvt" + nmos = "nfet_01v8" elif self.get_setting("vlsi.core.lvs_tool") == "hammer.lvs.netgen": pmos = "sky130_fd_pr__pfet_01v8_hvt" nmos = "sky130_fd_pr__nfet_01v8" From dc349734d74756d55fa0da3af5f3cea1c1c9fc8f Mon Sep 17 00:00:00 2001 From: elamdf Date: Mon, 23 Dec 2024 00:17:48 -0800 Subject: [PATCH 49/68] wording --- .github/workflows/e2e-cad.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e-cad.yml b/.github/workflows/e2e-cad.yml index 4402cc5f9..9b8a590c6 100644 --- a/.github/workflows/e2e-cad.yml +++ b/.github/workflows/e2e-cad.yml @@ -24,6 +24,8 @@ jobs: source $(poetry env info --path)/bin/activate # redirect all hammer output a temp file, since it contains proprietary tool logs tempfile=$(mktemp) + + # run cadence tools on design echo "log file is $tempfile on $(hostname)" | tee -a $tempfile echo "End-to-end CAD flow CI smoketest running on" ${{ github.head_ref }}.${{ github.sha }} > $tempfile 2>&1 make build >> $tempfile 2>&1 @@ -34,8 +36,7 @@ jobs: echo "running lvs.." | tee -a $tempfile make lvs >> $tempfile 2>&1 - # Verify DRC/LVS is clean (this is a very simple design, it should be clean without hacks) - + # Verify DRC, LVS are clean (this is a very simple design, it should be clean without hacks) echo "Checking that LVS passed..." | tee -a $tempfile # TODO these are probably pretty brittle... if [[ $(grep "Run Result : MATCH" "$tempfile") ]]; then From 0a3466298410a921b3af78c4cb038c4104752656 Mon Sep 17 00:00:00 2001 From: elamdf Date: Mon, 23 Dec 2024 10:47:40 -0800 Subject: [PATCH 50/68] add calibre paths and add output redir warning --- .github/workflows/e2e-cad.yml | 1 + e2e/configs-tool/cm.yml | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/e2e-cad.yml b/.github/workflows/e2e-cad.yml index 9b8a590c6..62af7ac23 100644 --- a/.github/workflows/e2e-cad.yml +++ b/.github/workflows/e2e-cad.yml @@ -26,6 +26,7 @@ jobs: tempfile=$(mktemp) # run cadence tools on design + # WARNING: do not create/modify tool invocations to output to stdout! All output should go to $tempfile echo "log file is $tempfile on $(hostname)" | tee -a $tempfile echo "End-to-end CAD flow CI smoketest running on" ${{ github.head_ref }}.${{ github.sha }} > $tempfile 2>&1 make build >> $tempfile 2>&1 diff --git a/e2e/configs-tool/cm.yml b/e2e/configs-tool/cm.yml index a1e35bf3c..3d9894110 100644 --- a/e2e/configs-tool/cm.yml +++ b/e2e/configs-tool/cm.yml @@ -9,5 +9,14 @@ vlsi.core.sim_tool: "hammer.sim.vcs" vlsi.core.timing_tool: "hammer.timing.tempus" vlsi.core.formal_tool: "hammer.formal.conformal" vlsi.core.power_tool: "hammer.power.voltus" +v#lsi.core.drc_tool: "hammer.drc.calibre" +#drc.calibre.version: "2022.2_24.16" +#vlsi.core.lvs_tool: "hammer.lvs.calibre" +#lvs.calibre.version: "2022.2_24.16" vlsi.core.drc_tool: "hammer.drc.pegasus" +drc.pegasus.version: "222" +drc.pegasus.pegasus_bin: "/tools/cadence/PEGASUS/PEGASUS222/bin/pegasus" vlsi.core.lvs_tool: "hammer.lvs.pegasus" +lvs.pegasus.pegasus_bin: "/tools/cadence/PEGASUS/PEGASUS222/bin/pegasus" +lvs.pegasus.version: "222" + From f3a4bab5ef1e10e8aed3a1c4cbb2e381724d5320 Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 12 Jan 2025 16:47:47 -0800 Subject: [PATCH 51/68] routing layer fixes --- hammer/technology/sky130/__init__.py | 9 +++++++++ hammer/technology/sky130/defaults.yml | 2 +- hammer/technology/sky130/sram-cache.json | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index a05755615..0f1b54149 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -946,6 +946,15 @@ def sky130_innovus_settings(ht: HammerTool) -> bool: """ ) + ht.append(""" +# note this is required for sky130_fd_sc_hd, the design has a ton of drcs if bottom layer is 1 + # TODO: why is setting routing_layer not enough? +set_db design_bottom_routing_layer 2 +set_db design_top_routing_layer 6 +# deprected syntax, but this used to always work +set_db route_design_bottom_routing_layer 2 + """) + return True diff --git a/hammer/technology/sky130/defaults.yml b/hammer/technology/sky130/defaults.yml index 8e21cdbf4..b3e718029 100644 --- a/hammer/technology/sky130/defaults.yml +++ b/hammer/technology/sky130/defaults.yml @@ -102,7 +102,7 @@ vlsi: tap_cell_interval: 15.0 # Set the interval and offset for tap cells tap_cell_offset: 5.0 - routing_layers: [1, 6] + routing_layers: [2, 6] technology.core: stackup: "sky130_fd_sc_hd" # This key should exist in the stackups list in the tech json diff --git a/hammer/technology/sky130/sram-cache.json b/hammer/technology/sky130/sram-cache.json index fdad68e08..150eb9f0d 100644 --- a/hammer/technology/sky130/sram-cache.json +++ b/hammer/technology/sky130/sram-cache.json @@ -682,5 +682,5 @@ "value": 1 } ] - }, + } ] From bf0460436918815a4d28e57c5f048f24138873f6 Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 26 Jan 2025 23:41:25 -0800 Subject: [PATCH 52/68] allow for arbitray fname overrides --- hammer/tech/__init__.py | 6 +++++- hammer/technology/sky130/defaults.yml | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/hammer/tech/__init__.py b/hammer/tech/__init__.py index 347e795de..1393db0ed 100644 --- a/hammer/tech/__init__.py +++ b/hammer/tech/__init__.py @@ -337,7 +337,11 @@ def override_tech_libraries(self) -> None: if self.get_setting("vlsi.technology.manually_override_pdk_collateral"): for lib in self.get_setting("vlsi.technology.override_libraries"): for key, path in lib['library'].items(): - fname = os.path.basename(path) + if isinstance(path, list): # path fname and fname to replace are different + fname = path[1] + path = path[0] + else: + fname = os.path.basename(path) if (key, fname) in manual_overrides: self.logger.error("Attempted to add {(key,path)} to overrides when {manual_overrides[(key, fname)]} already exists!") manual_overrides[(key, fname)] = path diff --git a/hammer/technology/sky130/defaults.yml b/hammer/technology/sky130/defaults.yml index b3e718029..25931fc38 100644 --- a/hammer/technology/sky130/defaults.yml +++ b/hammer/technology/sky130/defaults.yml @@ -43,6 +43,7 @@ technology.sky130: # WARNING: BREAK GLASS flag to override tech collateral with the paths provided in `override_libraries` vlsi.technology.manually_override_pdk_collateral: false vlsi.technology.override_libraries: [] # hacked collateral files to be used instead of default tech files with the same name +# either it's key:path or key:[path,name_to_override] if the path fname is diff than the fname to override mentor.extra_env_vars_meta: lazydeepsubst # Mentor environment variables # Override this in project From 969b9d8d799c4ee3450620a1696a696b69c46b2d Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 26 Jan 2025 23:41:43 -0800 Subject: [PATCH 53/68] re-enable io lef hack --- hammer/technology/sky130/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 625b2c75f..7c1dff880 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -470,7 +470,7 @@ def post_install_script(self) -> None: self.setup_cdl() self.setup_verilog() self.setup_techlef() - # self.setup_io_lefs() + self.setup_io_lefs() print("Loaded Sky130 Tech") def setup_cdl(self) -> None: From 93bdd623e4e385f49278495be6fbec44c3b0f085 Mon Sep 17 00:00:00 2001 From: elamdf Date: Fri, 31 Jan 2025 13:01:17 -0800 Subject: [PATCH 54/68] revert ci changes, these should be in a different PR --- .github/workflows/e2e-cad.yml | 56 -------------------- e2e/configs-design/pass/mock_hier.yml | 37 ++++++++++--- e2e/configs-design/pass/mock_hier_addl.yml | 6 +++ e2e/configs-design/pass/mock_hier_legacy.yml | 53 ++++++++++++++++++ e2e/configs-env/bwrc-env.yml | 25 +++------ e2e/configs-tool/cm.yml | 13 +---- e2e/pdks/sky130-bwrc.yml | 4 +- 7 files changed, 98 insertions(+), 96 deletions(-) delete mode 100644 .github/workflows/e2e-cad.yml create mode 100644 e2e/configs-design/pass/mock_hier_addl.yml create mode 100644 e2e/configs-design/pass/mock_hier_legacy.yml diff --git a/.github/workflows/e2e-cad.yml b/.github/workflows/e2e-cad.yml deleted file mode 100644 index 62af7ac23..000000000 --- a/.github/workflows/e2e-cad.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: End-to-end CAD flow smoketests - -on: - push: - branches: - - main - - test-ci - - sky130_pydantic - pull_request: - -jobs: - sky130-cm-e2e: - runs-on: [self-hosted] # TODO: replace with something centrally admined - - steps: - # Clone this repo - - name: Checkout - uses: actions/checkout@v4.1.6 - - - name: Run syn (Genus), par (Innovus), drc (Pegasus), and lvs (Pegasus) on the GCD design in sky130, using sky130_scl. - run: | - cd e2e - poetry install - source $(poetry env info --path)/bin/activate - # redirect all hammer output a temp file, since it contains proprietary tool logs - tempfile=$(mktemp) - - # run cadence tools on design - # WARNING: do not create/modify tool invocations to output to stdout! All output should go to $tempfile - echo "log file is $tempfile on $(hostname)" | tee -a $tempfile - echo "End-to-end CAD flow CI smoketest running on" ${{ github.head_ref }}.${{ github.sha }} > $tempfile 2>&1 - make build >> $tempfile 2>&1 - echo "running par.." | tee -a $tempfile - make par >> $tempfile 2>&1 - echo "running drc.." | tee -a $tempfile - make drc >> $tempfile 2>&1 - echo "running lvs.." | tee -a $tempfile - make lvs >> $tempfile 2>&1 - - # Verify DRC, LVS are clean (this is a very simple design, it should be clean without hacks) - echo "Checking that LVS passed..." | tee -a $tempfile - # TODO these are probably pretty brittle... - if [[ $(grep "Run Result : MATCH" "$tempfile") ]]; then - echo "LVS passed!" | tee -a $tempfile - else - echo "There's an LVS mismatch! see $tempfile for log" | tee -a $tempfile - exit 1 - fi - - echo "Checking that DRC is clean..." | tee -a $tempfile - if [[ $(grep "Total DRC Results : 0 (0)" "$tempfile") ]]; then - echo "DRC is clean!" | tee -a $tempfile - else - echo "There are DRC violations! see $tempfile for log" | tee -a $tempfile - exit 1 - fi diff --git a/e2e/configs-design/pass/mock_hier.yml b/e2e/configs-design/pass/mock_hier.yml index 40ef02692..96e381842 100644 --- a/e2e/configs-design/pass/mock_hier.yml +++ b/e2e/configs-design/pass/mock_hier.yml @@ -21,13 +21,36 @@ vlsi.inputs.hierarchical: - SubModD - SubModB: - SubModE - manual_placement_constraints: - - ChipTop: [] - - SubModA: [] - - SubModB: [] - - SubModC: [] - - SubModD: [] - - SubModE: [] + constraints: + - ChipTop: + vlsi.inputs.placement_constraints: + - path: ChipTop + type: toplevel + x: 0 + y: 0 + width: 1000 + height: 1000 + margins: + left: 0 + right: 0 + top: 0 + bottom: 0 + - SubModC: + vlsi.inputs.placement_constraints: + - path: SubModC + type: toplevel + x: 0 + y: 0 + width: 100 + height: 100 + margins: + left: 0 + right: 0 + top: 0 + bottom: 0 + vlsi.inputs.power_spec_type: manual + vlsi.inputs.power_spec_contents: fake_command + par.generate_power_straps_options.by_tracks.strap_layers: [m3, m4, m5] vlsi.core.synthesis_tool: "hammer.synthesis.mocksynth" synthesis.mocksynth.temp_folder: "obj_dir" diff --git a/e2e/configs-design/pass/mock_hier_addl.yml b/e2e/configs-design/pass/mock_hier_addl.yml new file mode 100644 index 000000000..1a38c462f --- /dev/null +++ b/e2e/configs-design/pass/mock_hier_addl.yml @@ -0,0 +1,6 @@ +vlsi.inputs.hierarchical.constraints_meta: append +vlsi.inputs.hierarchical.constraints: + - SubModC: + vlsi.inputs.power_spec_type: upf + par.generate_power_straps_options.by_tracks.strap_layers: [m6] + par.generate_power_straps_options.by_tracks.strap_layers_meta: append \ No newline at end of file diff --git a/e2e/configs-design/pass/mock_hier_legacy.yml b/e2e/configs-design/pass/mock_hier_legacy.yml new file mode 100644 index 000000000..914fc9b7e --- /dev/null +++ b/e2e/configs-design/pass/mock_hier_legacy.yml @@ -0,0 +1,53 @@ +# Generate Make include to aid in flow +vlsi.core.build_system: make + +vlsi.inputs.power_spec_type: "cpf" +vlsi.inputs.power_spec_mode: "auto" + +synthesis.inputs: + top_module: "pass" + input_files: ["src/pass.v"] + +vlsi.inputs.hierarchical: + mode: hierarchical + top_module: ChipTop + config_source: manual + manual_modules: + - ChipTop: + - SubModA + - SubModB + - SubModA: + - SubModC + - SubModD + - SubModB: + - SubModE + manual_placement_constraints: + - ChipTop: [] + - SubModA: [] + - SubModB: [] + - SubModC: + - path: SubModC + type: toplevel + x: 0 + y: 0 + width: 100 + height: 100 + margins: + left: 0 + right: 0 + top: 0 + bottom: 0 + - SubModD: [] + - SubModE: [] + constraints: + - SubModC: + vlsi.inputs.power_spec_type: manual + vlsi.inputs.power_spec_contents: fake_command + par.generate_power_straps_options.by_tracks.strap_layers: [m3, m4, m5] + +vlsi.core.synthesis_tool: "hammer.synthesis.mocksynth" +synthesis.mocksynth.temp_folder: "obj_dir" +vlsi.core.par_tool: "hammer.par.mockpar" +vlsi.core.sim_tool: "hammer.sim.mocksim" +vlsi.core.drc_tool: "hammer.drc.mockdrc" +vlsi.core.lvs_tool: "hammer.lvs.mocklvs" diff --git a/e2e/configs-env/bwrc-env.yml b/e2e/configs-env/bwrc-env.yml index 1bc3686f8..613304ede 100644 --- a/e2e/configs-env/bwrc-env.yml +++ b/e2e/configs-env/bwrc-env.yml @@ -27,26 +27,13 @@ drc.magic.magic_bin: "/tools/commercial/skywater/local/chipyard-tutorial/.conda- lvs.netgen.netgen_bin: "/tools/commercial/skywater/local/chipyard-tutorial/.conda-signoff/bin/netgen" -# Technology paths +# Sky130 paths technology.sky130: - #sky130A: "/tools/commercial/skywater/local/open_pdks-2022.10/share/pdk/sky130A" - # sram22_sky130_macros: "/tools/commercial/skywater/local/chipyard-tutorial/sram22_sky130_macros" - sram22_sky130_macros: "/tools/C/rohankumar/stac-top/vlsi/sram22_sky130_macros/" - # https://github.com/rahulk29/sram22_sky130_macros/tree/dev - - # this key is OPTIONAL, no NDA files will be used if it does not point to a valid path - #sky130_nda: "/tools/commercial/skywater/swtech130/skywater-src-nda" - - # for caravel collateral pulled in by this design - caravel: /tools/commercial/skywater/local/caravel/v6.0 - - lvs_blackbox_srams: true - sky130_scl: "/tools/C/elamdf/sky130/sky130_scl_9T_0.0.6" - sky130_cds: "/tools/C/elamdf/sky130/sky130_release_0.0.4/" - stdcell_library: "sky130_scl" - -technology.core.stackup: "sky130_scl" -vlsi.technology.placement_site: "CoreSite" + sky130A: "/tools/commercial/skywater/local/sky130A" + sram22_sky130_macros: "/tools/commercial/skywater/local/sram22_sky130_macros" + openram_lib: "/tools/commercial/skywater/local/sky130_sram_macros" + sky130_nda: "/tools/commercial/skywater/swtech130/skywater-src-nda-20221031" + sky130_cds: "/tools/commercial/skywater/sky130_cds/sky130_prelim_release_091123" # ASAP7 paths technology.asap7: diff --git a/e2e/configs-tool/cm.yml b/e2e/configs-tool/cm.yml index 3d9894110..df7785586 100644 --- a/e2e/configs-tool/cm.yml +++ b/e2e/configs-tool/cm.yml @@ -9,14 +9,5 @@ vlsi.core.sim_tool: "hammer.sim.vcs" vlsi.core.timing_tool: "hammer.timing.tempus" vlsi.core.formal_tool: "hammer.formal.conformal" vlsi.core.power_tool: "hammer.power.voltus" -v#lsi.core.drc_tool: "hammer.drc.calibre" -#drc.calibre.version: "2022.2_24.16" -#vlsi.core.lvs_tool: "hammer.lvs.calibre" -#lvs.calibre.version: "2022.2_24.16" -vlsi.core.drc_tool: "hammer.drc.pegasus" -drc.pegasus.version: "222" -drc.pegasus.pegasus_bin: "/tools/cadence/PEGASUS/PEGASUS222/bin/pegasus" -vlsi.core.lvs_tool: "hammer.lvs.pegasus" -lvs.pegasus.pegasus_bin: "/tools/cadence/PEGASUS/PEGASUS222/bin/pegasus" -lvs.pegasus.version: "222" - +vlsi.core.drc_tool: "hammer.drc.calibre" +vlsi.core.lvs_tool: "hammer.lvs.calibre" diff --git a/e2e/pdks/sky130-bwrc.yml b/e2e/pdks/sky130-bwrc.yml index 0d75b7862..e7feb9a04 100644 --- a/e2e/pdks/sky130-bwrc.yml +++ b/e2e/pdks/sky130-bwrc.yml @@ -5,10 +5,8 @@ technology.sky130: openram_lib: "/tools/commercial/skywater/local/sky130_sram_macros" sky130_nda: "/tools/commercial/skywater/swtech130/skywater-src-nda-20221031" sky130_cds: "/tools/commercial/skywater/sky130_cds/sky130_prelim_release_091123" - sky130_cds: "/tools/commercial/skywater/sky130_cds/PDK/sky130_release_0.0.3/" - sky130_scl: "/tools/commercial/skywater/sky130_cds/LIB/sky130_scl_9T_0.0.3/" -synthesis.genus.version: "221" +synthesis.genus.version: "211" par.innovus.version: "211" sim.vcs.version: "S-2021.09-SP1-1" From a69a7483099d54028bda3b7f904955d7ca0f18c6 Mon Sep 17 00:00:00 2001 From: elamdf Date: Fri, 31 Jan 2025 15:29:23 -0800 Subject: [PATCH 55/68] allow lvs,drc deck paths to be auto-overwritten by cache --- hammer/tech/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hammer/tech/__init__.py b/hammer/tech/__init__.py index f895ebfc3..0b14127b2 100644 --- a/hammer/tech/__init__.py +++ b/hammer/tech/__init__.py @@ -351,9 +351,9 @@ def override_tech_libraries(self) -> None: self.logger.warning("You've attempted to specify override libraries without enabling vlsi.technology.manually_override_pdk_collateral! collateral paths will not be overwritten") used_overrides = [] - for lib in self.config.libraries: + for lib in self.config.libraries + self.config.drc_decks + self.config.lvs_decks: for field_name in lib.model_fields: - if field_name.endswith("_file") or field_name == "verilog_sim": + if "file" in field_name or "path" in field_name or field_name == "verilog_sim": # check if that file exists in cache, override if so default_path = getattr(lib, field_name) if not default_path: From 871a44fc539b5381c210b4dde3e6b044aa4cf722 Mon Sep 17 00:00:00 2001 From: elamdf Date: Fri, 31 Jan 2025 15:29:37 -0800 Subject: [PATCH 56/68] re-enable calibre deck hack --- hammer/technology/sky130/__init__.py | 112 ++++++++------------------- 1 file changed, 34 insertions(+), 78 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 83b6f30b0..b0f2e37fc 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -490,8 +490,42 @@ def post_install_script(self) -> None: self.setup_verilog() self.setup_techlef() self.setup_io_lefs() + self.setup_calibre_lvs_deck() print("Loaded Sky130 Tech") + def setup_calibre_lvs_deck(self) -> bool: + # Remove conflicting specification statements found in PDK LVS decks + pattern = ".*({}).*\n".format("|".join(LVS_DECK_SCRUB_LINES)) + matcher = re.compile(pattern) + + source_paths = self.get_setting("technology.sky130.lvs_deck_sources") + lvs_decks = self.config.lvs_decks + if not lvs_decks: + return True + for i, deck in enumerate(lvs_decks): + if deck.tool_name != "calibre": + continue + try: + source_path = Path(source_paths[i]) + except IndexError: + self.logger.error( + "No corresponding source for LVS deck {}".format(deck)) + continue + if not source_path.exists(): + raise FileNotFoundError(f"LVS deck not found: {source_path}") + cache_tech_dir_path = Path(self.cache_dir) + dest_path = os.path.join(cache_tech_dir_path, + os.path.basename(deck.path)) + with open(source_path, "r") as sf: + with open(dest_path, "w") as df: + self.logger.info( + "Modifying LVS deck: {} -> {}".format( + source_path, dest_path) + ) + df.write(matcher.sub("", sf.read())) + df.write(LVS_DECK_INSERT_LINES) + return True + def setup_cdl(self) -> None: """Copy and hack the cdl, replacing pfet_01v8_hvt/nfet_01v8 with respective names in LVS deck @@ -795,11 +829,6 @@ def get_tech_drc_hooks(self, tool_name: str) -> List[HammerToolHookAction]: return hooks.get(tool_name, []) def get_tech_lvs_hooks(self, tool_name: str) -> List[HammerToolHookAction]: - # calibre_hooks = [ - # HammerTool.make_post_insertion_hook( - # "generate_lvs_run_file", setup_calibre_lvs_deck - # ) - # ] calibre_hooks = [] pegasus_hooks = [] if self.use_sram22: @@ -1055,7 +1084,6 @@ def set_cts_base_cells(ht: HammerTool) -> bool: # TODO: this should just be placign rail straps, so higher straps are placed by non-hardocded tcl def power_rail_straps_no_tapcells(ht: HammerTool) -> bool: # We do this since there are no explicit tapcells in sky130_scl - # just need the rail ones, others are placed as usual. ht.append( """ @@ -1119,44 +1147,6 @@ def power_rail_straps_no_tapcells(ht: HammerTool) -> bool: return True -# TODO do we want this to be in upstream hammer? -# def efabless_ring_io(ht: HammerTool) -> bool: -# assert isinstance(ht, HammerPlaceAndRouteTool), "IO ring instantiation only for par" -# assert isinstance(ht, TCLTool), "IO ring instantiation can only run on TCL tools" -# io_file = ht.get_setting("technology.sky130.io_file") -# ht.append(f"read_io_file {io_file} -no_die_size_adjust") -# p_nets = list(map(lambda s: s.name, ht.get_independent_power_nets())) -# g_nets = list(map(lambda s: s.name, ht.get_independent_ground_nets())) -# ht.append(f''' -# # Global net connections -# connect_global_net VDDA -type pg_pin -pin_base_name VDDA -verbose -# connect_global_net VDDIO -type pg_pin -pin_base_name VDDIO* -verbose -# connect_global_net {p_nets[0]} -type pg_pin -pin_base_name VCCD* -verbose -# connect_global_net {p_nets[0]} -type pg_pin -pin_base_name VCCHIB -verbose -# connect_global_net {p_nets[0]} -type pg_pin -pin_base_name VSWITCH -verbose -# connect_global_net {g_nets[0]} -type pg_pin -pin_base_name VSSA -verbose -# connect_global_net {g_nets[0]} -type pg_pin -pin_base_name VSSIO* -verbose -# connect_global_net {g_nets[0]} -type pg_pin -pin_base_name VSSD* -verbose -# ''') -# ht.append(''' -# # IO fillers -# set io_fillers {sky130_ef_io__com_bus_slice_20um sky130_ef_io__com_bus_slice_10um sky130_ef_io__com_bus_slice_5um sky130_ef_io__com_bus_slice_1um} -# add_io_fillers -io_ring 1 -cells $io_fillers -side top -filler_orient r0 -# add_io_fillers -io_ring 1 -cells $io_fillers -side right -filler_orient r270 -# add_io_fillers -io_ring 1 -cells $io_fillers -side bottom -filler_orient r180 -# add_io_fillers -io_ring 1 -cells $io_fillers -side left -filler_orient r90 -# ''') -# ht.append(f''' -# # Core ring -# add_rings -follow io -layer met5 -nets {{ {p_nets[0]} {g_nets[0]} }} -offset 5 -width 13 -spacing 3 -# route_special -connect pad_pin -nets {{ {p_nets[0]} {g_nets[0]} }} -detailed_log -# ''') -# ht.append(''' -# # Prevent buffering on TIE_LO_ESD and TIE_HI_ESD -# set_dont_touch [get_db [get_db pins -if {.name == *TIE*ESD}] .net] -# ''') -# return True - def calibre_drc_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerDRCTool), "Exlude SRAMs only in DRC" @@ -1268,39 +1258,5 @@ def sram22_lvs_recognize_gates_all(ht: HammerTool) -> bool: return True -def setup_calibre_lvs_deck(ht: HammerTool) -> bool: - assert isinstance( - ht, HammerLVSTool), "Modify Calibre LVS deck for LVS only" - # Remove conflicting specification statements found in PDK LVS decks - pattern = ".*({}).*\n".format("|".join(LVS_DECK_SCRUB_LINES)) - matcher = re.compile(pattern) - - source_paths = ht.get_setting("technology.sky130.lvs_deck_sources") - lvs_decks = ht.technology.config.lvs_decks - if not lvs_decks: - return True - for i, deck in enumerate(lvs_decks): - if deck.tool_name != "calibre": - continue - try: - source_path = Path(source_paths[i]) - except IndexError: - ht.logger.error( - "No corresponding source for LVS deck {}".format(deck)) - continue - if not source_path.exists(): - raise FileNotFoundError(f"LVS deck not found: {source_path}") - dest_path = deck.path - ht.technology.ensure_dirs_exist(dest_path) - with open(source_path, "r") as sf: - with open(dest_path, "w") as df: - ht.logger.info( - "Modifying LVS deck: {} -> {}".format( - source_path, dest_path) - ) - df.write(matcher.sub("", sf.read())) - df.write(LVS_DECK_INSERT_LINES) - return True - tech = SKY130Tech() From ff7090cada0f2f928c0c38a87923fbf0e263c71a Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 2 Feb 2025 09:43:07 -0800 Subject: [PATCH 57/68] fix unterminated stirng literals --- .../sky130/sram_compiler/__init__.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/hammer/technology/sky130/sram_compiler/__init__.py b/hammer/technology/sky130/sram_compiler/__init__.py index 12e63be59..29b544433 100644 --- a/hammer/technology/sky130/sram_compiler/__init__.py +++ b/hammer/technology/sky130/sram_compiler/__init__.py @@ -76,8 +76,8 @@ def generate_sram(self, params: SRAMParameters, corner: MMMCCorner) -> ExtraLibr ) if not os.path.exists(lib_path): self.logger.error( - f"SKY130 {params.family} SRAM cache does not support corner: { - corner_str}" + f"""SKY130 {params.family} SRAM cache does not support corner: { + corner_str}""" ) lef_file = "{b}/{n}/{n}.lef".format(b=base_dir, n=sram_name) @@ -127,8 +127,8 @@ def generate_sram(self, params: SRAMParameters, corner: MMMCCorner) -> ExtraLibr ) if not os.path.exists(lib_path): self.logger.error( - f"SKY130 {params.family} SRAM cache does not support corner: { - corner_str}" + f"""SKY130 {params.family} SRAM cache does not support corner: { + corner_str}""" ) self.setup_openram_spice(sram_name) @@ -168,8 +168,8 @@ def setup_openram_spice(self, sram_name) -> None: / sram_name / f"{sram_name}.lvs.sp" ) - dest_path = f"{os.path.abspath( - self.technology.cache_dir)}/{sram_name}/{sram_name}.lvs.sp" + dest_path = f"""{os.path.abspath( + self.technology.cache_dir)}/{sram_name}/{sram_name}.lvs.sp""" self.technology.ensure_dirs_exist(dest_path) if not source_path.exists(): raise FileNotFoundError(f"SRAM Spice file not found: {source_path}") @@ -191,8 +191,8 @@ def setup_openram_lef(self, sram_name) -> None: / sram_name / f"{sram_name}.lef" ) - dest_path = f"{os.path.abspath(self.technology.cache_dir) - }/{sram_name}/{sram_name}.lef" + dest_path = f"""{os.path.abspath(self.technology.cache_dir) + }/{sram_name}/{sram_name}.lef""" self.technology.ensure_dirs_exist(dest_path) if not source_path.exists(): raise FileNotFoundError(f"SRAM LEF file not found: {source_path}") @@ -218,8 +218,8 @@ def setup_openram_verilog(self, sram_name) -> None: / sram_name / f"{sram_name}.v" ) - dest_path = f"{os.path.abspath(self.technology.cache_dir) - }/{sram_name}/{sram_name}.v" + dest_path = f"""{os.path.abspath(self.technology.cache_dir) + }/{sram_name}/{sram_name}.v""" if not source_path.exists(): raise FileNotFoundError(f"SRAM Spice file not found: {source_path}") self.technology.ensure_dirs_exist(dest_path) @@ -247,8 +247,8 @@ def setup_sram_lib(self, sram_name) -> None: / sram_name / f"{sram_name}_TT_1p8V_25C.lib" ) - dest_path = f"{os.path.abspath(self.technology.cache_dir) - }/{sram_name}/{sram_name}.v" + dest_path = f"""{os.path.abspath(self.technology.cache_dir) + }/{sram_name}/{sram_name}.v""" if not source_path.exists(): raise FileNotFoundError(f"SRAM Lib file not found: {source_path}") self.technology.ensure_dirs_exist(dest_path) From f29444f5b2a23ee7e9b65c0527bd4cb1088ec912 Mon Sep 17 00:00:00 2001 From: elamdf Date: Sun, 2 Feb 2025 09:49:39 -0800 Subject: [PATCH 58/68] [untested] write only rails using hardcoded tcl for sky130_scl --- hammer/technology/sky130/__init__.py | 52 ++-------------------------- 1 file changed, 2 insertions(+), 50 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index b0f2e37fc..12e8e0aa9 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -789,7 +789,7 @@ def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]: if self.get_setting("technology.sky130.stdcell_library") == "sky130_scl": hooks["innovus"].extend( [ - HammerTool.make_replacement_hook( + HammerTool.make_pre_insertion_hook( "power_straps", power_rail_straps_no_tapcells ), HammerTool.make_pre_insertion_hook( @@ -1083,65 +1083,17 @@ def set_cts_base_cells(ht: HammerTool) -> bool: # TODO: this should just be placign rail straps, so higher straps are placed by non-hardocded tcl def power_rail_straps_no_tapcells(ht: HammerTool) -> bool: + assert not ht.get_setting("par.generate_power_straps_options.by_tracks.generate_rail_layer"), "rails must be placed by this hook for sky130_scl!" # We do this since there are no explicit tapcells in sky130_scl # just need the rail ones, others are placed as usual. ht.append( """ -# -------------------------------------------------------------------------------- -# This script was written and developed by HAMMER at UC Berkeley; however, the -# underlying commands and reports are copyrighted by Cadence. We thank Cadence for -# granting permission to share our research to help promote and foster the next -# generation of innovators. -# -------------------------------------------------------------------------------- - # Power strap definition for layer met1 (rails): - # should be .14 set_db add_stripes_stacked_via_top_layer met1 set_db add_stripes_stacked_via_bottom_layer met1 set_db add_stripes_spacing_from_block 4.000 -#add_stripes -over_physical_pins 1 -nets {VDD VSS} -width .14 -direction horizontal -pin_layer met1 -layer met1 -#add_stripes -layer met1 -over_pins 1 -number_of_sets 1 -spacing 3.74 -direction horizontal -width .4 -nets { VSS VDD } -number_of_sets 1 -#add_stripes -pin_layer met1 -layer met1 -over_pins 1 -spacing .2 -direction horizontal -width .4 -nets { VSS VDD } -#add_stripes -master "FILL*" -over_pins 1 -block_ring_bottom_layer_limit met1 -block_ring_top_layer_limit met1 -direction horizontal -layer met1 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met1 -width pin_width -#add_stripes -nets {VDD VSS} -layer met1 -direction horizontal -width .4 -spacing 4.54 -set_to_set_distance 9.08 -start_from bottom -pin_offset -2.46 -#add_stripes -nets {VDD VSS} -layer met1 -direction horizontal -width .4 -spacing 3.74 -number_of_sets 1 -start_from left -switch_layer_over_obs false -max_same_layer_jog_length 2 -pad_core_ring_top_layer_limit met5 -pad_core_ring_bottom_layer_limit met1 -block_ring_top_layer_limit met5 -block_ring_bottom_layer_limit met1 -use_wire_group 0 -snap_wire_center_to_grid none add_stripes -nets {VDD VSS} -layer met1 -direction horizontal -start_offset -.2 -width .4 -spacing 3.74 -set_to_set_distance 8.28 -start_from bottom -switch_layer_over_obs false -max_same_layer_jog_length 2 -pad_core_ring_top_layer_limit met5 -pad_core_ring_bottom_layer_limit met1 -block_ring_top_layer_limit met5 -block_ring_bottom_layer_limit met1 -use_wire_group 0 -snap_wire_center_to_grid none - -# Power strap definition for layer met2: - -#set_db add_stripes_stacked_via_top_layer met2 -#set_db add_stripes_stacked_via_bottom_layer met1 -#set_db add_stripes_trim_antenna_back_to_shape {stripe} -##set_db add_stripes_spacing_from_block 4.000 -##add_stripes -create_pins 0 -block_ring_bottom_layer_limit met2 -block_ring_top_layer_limit met1 -direction vertical -layer met2 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met1 -set_to_set_distance 101.20 -spacing 2.26 -switch_layer_over_obs 0 -width 1.42 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 0] + 4.81] -#add_stripes -nets {VDD VSS} -layer met2 -direction vertical -width .2 -spacing 0.14 -number_of_sets 1 -extend_to all_domains -start_from left -switch_layer_over_obs false -max_same_layer_jog_length 2 -pad_core_ring_top_layer_limit met5 -pad_core_ring_bottom_layer_limit met1 -block_ring_top_layer_limit met5 -block_ring_bottom_layer_limit met1 -use_wire_group 0 -snap_wire_center_to_grid none - -## Power strap definition for layer met3: - -#set_db add_stripes_stacked_via_top_layer met3 -#set_db add_stripes_stacked_via_bottom_layer met2 -#set_db add_stripes_trim_antenna_back_to_shape {stripe} -#set_db add_stripes_spacing_from_block 2.000 -#add_stripes -create_pins 0 -block_ring_bottom_layer_limit met3 -block_ring_top_layer_limit met2 -direction horizontal -layer met3 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met2 -set_to_set_distance 75.90 -spacing 3.66 -switch_layer_over_obs 0 -width 1.86 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 1] + 7.35] - -# Power strap definition for layer met4: - -set_db add_stripes_stacked_via_top_layer met4 -set_db add_stripes_stacked_via_bottom_layer met1 -set_db add_stripes_trim_antenna_back_to_shape {stripe} -set_db add_stripes_spacing_from_block 2.000 -#add_stripes -create_pins 0 -block_ring_bottom_layer_limit met4 -block_ring_top_layer_limit met3 -direction vertical -layer met4 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met3 -set_to_set_distance 75.90 -spacing 3.66 -switch_layer_over_obs 0 -width 1.86 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 0] + 7.35] -add_stripes -create_pins 0 -block_ring_bottom_layer_limit met4 -block_ring_top_layer_limit met1 -direction vertical -layer met4 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met1 -set_to_set_distance 75.90 -spacing 3.66 -switch_layer_over_obs 0 -width 1.86 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 0] + 7.35] - -# Power strap definition for layer met5: - -set_db add_stripes_stacked_via_top_layer met5 -set_db add_stripes_stacked_via_bottom_layer met4 -set_db add_stripes_trim_antenna_back_to_shape {stripe} -set_db add_stripes_spacing_from_block 2.000 -add_stripes -create_pins 1 -block_ring_bottom_layer_limit met5 -block_ring_top_layer_limit met4 -direction horizontal -layer met5 -nets {VSS VDD} -pad_core_ring_bottom_layer_limit met4 -set_to_set_distance 225.40 -spacing 17.68 -switch_layer_over_obs 0 -width 1.64 -area [get_db designs .core_bbox] -start [expr [lindex [lindex [get_db designs .core_bbox] 0] 1] + 5.62] - """ ) return True From a41b44b75aa7acdafa6563f4105a2056212a12ba Mon Sep 17 00:00:00 2001 From: Elam Date: Sun, 2 Feb 2025 22:14:43 -0800 Subject: [PATCH 59/68] move hvl ls hack from hammer-driver to tech init --- hammer/technology/sky130/__init__.py | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 12e8e0aa9..7b91594c0 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -491,6 +491,7 @@ def post_install_script(self) -> None: self.setup_techlef() self.setup_io_lefs() self.setup_calibre_lvs_deck() + self.setup_hvl_ls_lef() print("Loaded Sky130 Tech") def setup_calibre_lvs_deck(self) -> bool: @@ -772,6 +773,35 @@ def setup_io_lefs(self) -> None: df.writelines(sl) + def setup_hvl_ls_lef(self) -> bool: + # Treat HVL cells as if they were hard macros to avoid needing to set them + # up "properly" with multiple power domains + + lef_name = "sky130_fd_sc_hvl__lsbufhv2lv_1.lef" + + sky130A_path = Path(self.get_setting('technology.sky130.sky130A')) + source_path = sky130A_path / 'libs.ref' / 'sky130_fd_sc_hvl' / 'lef' / "sky130_fd_sc_hvl.lef" + cache_path = Path(self.cache_dir) / "fd_sc_hvl__lef" / lef_name + cache_path.parent.mkdir(exist_ok=True) + + with source_path.open("r") as sf, cache_path.open("w") as df: + self.logger.info(f"Patching HVL LS LEF: {source_path} -> {cache_path}") + is_in_site_def = False + is_in_macro_def = False + for line in sf: + if is_in_site_def: + if 'END unithv' in line: + is_in_site_def = False + elif not is_in_macro_def and 'SITE unithv' in line: + is_in_site_def = True + elif 'MACRO ' in line: + is_in_macro_def = True + df.write(line) + elif 'SITE unithv' in line: + pass + else: + df.write(line.replace("CLASS CORE", "CLASS BLOCK") if not (("ANTENNACELL" in line) or ("SPACER" in line)) else line) + def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]: hooks = { "innovus": [ From f936cfdb68d3c73977e833bfcb0b253601bc38fb Mon Sep 17 00:00:00 2001 From: Elam Date: Sun, 2 Feb 2025 22:56:41 -0800 Subject: [PATCH 60/68] deduplicate override logging --- hammer/tech/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hammer/tech/__init__.py b/hammer/tech/__init__.py index 0b14127b2..66c35ede3 100644 --- a/hammer/tech/__init__.py +++ b/hammer/tech/__init__.py @@ -350,7 +350,7 @@ def override_tech_libraries(self) -> None: if len(self.get_setting("vlsi.technology.override_libraries")) > 0: self.logger.warning("You've attempted to specify override libraries without enabling vlsi.technology.manually_override_pdk_collateral! collateral paths will not be overwritten") - used_overrides = [] + used_overrides = {} for lib in self.config.libraries + self.config.drc_decks + self.config.lvs_decks: for field_name in lib.model_fields: if "file" in field_name or "path" in field_name or field_name == "verilog_sim": @@ -369,11 +369,8 @@ def override_tech_libraries(self) -> None: if len(cached_paths) > 1: self.logger.error(f"ambiguous cache override: {cached_paths}") if cached_paths: - used_overrides.append((field_name, fname)) new_path = cached_paths[0] - self.logger.info( - f"overriding {default_path} with cache entry {new_path}" - ) + used_overrides[(field_name, fname)] = (new_path, False) # non-manual override manual_override_paths = set([f for f in fnames if (field_name, f) in manual_overrides]) manual_override_paths = [manual_overrides[(field_name, f)] for f in manual_override_paths] @@ -383,13 +380,16 @@ def override_tech_libraries(self) -> None: self.logger.error(f"ambiguous cache override: {manual_override_paths}") # prioritize manual overrides over cache if manual_override_paths: - used_overrides.append((field_name, fname)) new_path = manual_override_paths[0] - self.logger.info( - f"overriding {default_path} with manual override {new_path}" - ) + used_overrides[(field_name, fname)] = (new_path, True) # manual override setattr(lib, field_name, new_path) + # do logging at the end to prevent spamming stdout with the same overrides + for field, new_path in used_overrides.items(): + override_type = "manual" if new_path[1] else "cache" + self.logger.info( + f"Overriding {field[0]} {field[1]} with {override_type} path {new_path[0]}" + ) unused_overrides = [k for k in manual_overrides.keys() if k not in used_overrides] if unused_overrides: self.logger.warning(f"Unused tech collateral overrides: {unused_overrides}") From 8b054b26342a4247b8fb806f801015aaceeecaaf Mon Sep 17 00:00:00 2001 From: Elam Date: Mon, 3 Feb 2025 09:44:00 -0800 Subject: [PATCH 61/68] fix duplicate override error check --- hammer/tech/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hammer/tech/__init__.py b/hammer/tech/__init__.py index 66c35ede3..ec0ad7b61 100644 --- a/hammer/tech/__init__.py +++ b/hammer/tech/__init__.py @@ -343,8 +343,8 @@ def override_tech_libraries(self) -> None: path = path[0] else: fname = os.path.basename(path) - if (key, fname) in manual_overrides: - self.logger.error("Attempted to add {(key,path)} to overrides when {manual_overrides[(key, fname)]} already exists!") + if (key, fname) in manual_overrides and manual_overrides[(key,fname)] != path: + self.logger.error(f"Attempted to add {(key,path)} to overrides when {manual_overrides[(key, fname)]} already exists!") manual_overrides[(key, fname)] = path else: if len(self.get_setting("vlsi.technology.override_libraries")) > 0: From e1eb0f9e0e891c78bccc3dc10b2c75e8d11b9ad9 Mon Sep 17 00:00:00 2001 From: elamdf Date: Wed, 19 Feb 2025 13:28:34 -0800 Subject: [PATCH 62/68] rename techjson to techconfig --- .readthedocs.yml | 2 +- doc/Hammer-Basics/Hammer-Setup.md | 2 +- doc/Technology/Tech-json.rst | 20 ++++++++++---------- hammer/tech/__init__.py | 10 +++++----- hammer/technology/sky130/__init__.py | 5 ++--- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index e1ce84e00..7c99bd41b 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -13,7 +13,7 @@ build: # VIRTUAL_ENV needs to be set manually for now. # See https://github.com/readthedocs/readthedocs.org/pull/11152/ - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install - - python3 -c "from hammer.tech import TechJSON; print(TechJSON.schema_json(indent=2))" > doc/Technology/schema.json + - python3 -c "from hammer.tech import TechConfig; print(TechConfig.schema_json(indent=2))" > doc/Technology/schema.json # Build documentation with Sphinx sphinx: diff --git a/doc/Hammer-Basics/Hammer-Setup.md b/doc/Hammer-Basics/Hammer-Setup.md index 4a3b7ddda..267c8ca03 100644 --- a/doc/Hammer-Basics/Hammer-Setup.md +++ b/doc/Hammer-Basics/Hammer-Setup.md @@ -205,7 +205,7 @@ Run `poetry update` and `poetry install` and commit `poetry.lock`. First, generate the `schema.json` file from within your poetry virtualenv: ```shell -python3 -c "from hammer.tech import TechJSON; print(TechJSON.schema_json(indent=2))" > doc/Technology/schema.json +python3 -c "from hammer.tech import TechConfig; print(TechConfig.schema_json(indent=2))" > doc/Technology/schema.json ``` Then: diff --git a/doc/Technology/Tech-json.rst b/doc/Technology/Tech-json.rst index 2c5388906..035b3d49e 100644 --- a/doc/Technology/Tech-json.rst +++ b/doc/Technology/Tech-json.rst @@ -4,9 +4,9 @@ Hammer Tech Config (Tech JSON) =============================== Technology plugins must set up some general information about the install of the PDK, set up DRC rule decks, set up pointers to PDK files, and supply technology stackup information. -Formerly, this information was provided in a static ``.tech.json`` file. We now encourage you to generate this information in the form of ``TechJSON`` Pydantic BaseModel instances (see ``hammer/tech/__init__.py`` for all BaseModel definitions). Instructions are given below for both forms, with examples from the Sky130 and ASAP7 plugins. +Formerly, this information was provided in a static ``.tech.json`` file. We now encourage you to generate this information in the form of ``TechConfig`` Pydantic BaseModel instances (see ``hammer/tech/__init__.py`` for all BaseModel definitions). Instructions are given below for both forms, with examples from the Sky130 and ASAP7 plugins. -For the full schema of the tech configuration, please see the :ref:`full_schema` section below, which is derived from ``TechJSON``. +For the full schema of the tech configuration, please see the :ref:`full_schema` section below, which is derived from ``TechConfig``. Technology Install --------------------------------- @@ -34,7 +34,7 @@ Pydantic: def gen_config(self) -> None: #... - self.config = TechJSON( + self.config = TechConfig( name = "Skywater 130nm Library", grid_unit = "0.001", installs = [ @@ -85,7 +85,7 @@ Pydantic: def gen_config(self) -> None: #... - self.config = TechJSON( + self.config = TechConfig( #fields skipped... drc_decks = [ DRCDeck(tool_name = "calibre", deck_name = "calibre_drc", path = "$SKY130_NDA/s8/V2.0.1/DRC/Calibre/s8_drcRules"), @@ -116,7 +116,7 @@ The file pointers, in this case, use the tarball prefix because Hammer will be e Library Setup --------------------------------- -The ``libraries`` Field also must be set in the TechJSON instance. This will tell Hammer where to find all of the relevant files for standard cells and other blocks for the VLSI flow. Path prefixes are used most heavily here. +The ``libraries`` Field also must be set in the TechConfig instance. This will tell Hammer where to find all of the relevant files for standard cells and other blocks for the VLSI flow. Path prefixes are used most heavily here. The ``corner`` Field (BaseModel type: Corner) tells Hammer what process and temperature corner that these files correspond to. The ``supplies`` Field (BaseModel type: Supplies) tells Hammer what the nominal supply for these cells are. The ``provides`` Field (type: List[Provide]) has several sub-fields that tell Hammer what kind of library this is (examples include ``stdcell``, ``fiducials``, ``io pad cells``, ``bump``, and ``level shifters``) and the threshold voltage flavor of the cells, if applicable. @@ -160,7 +160,7 @@ Pydantic: ) libs.append(lib_entry) #... - self.config = TechJSON( + self.config = TechConfig( #fields skipped... libraries = libs, #fields skipped... @@ -237,7 +237,7 @@ You can use ``LEFUtils.get_metals`` to generate the stackup information for simp metals = list(map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path))) stackups.append(Stackup(name = library, grid_unit = Decimal("0.001"), metals = metals)) - self.config = TechJSON( + self.config = TechConfig( #fields skipped... stackups = stackups, #fields skipped... @@ -261,7 +261,7 @@ The ``sites`` field specifies the unit standard cell size of the technology for def gen_config(self) -> None: #... - self.config = TechJSON( + self.config = TechConfig( #fields skipped... sites = [ Site(name = "unithd", x = Decimal("0.46"), y = Decimal("2.72")), @@ -287,7 +287,7 @@ The example below shows a subset of the Sky130 and ASAP7 tech plugin for 2 types def gen_config(self) -> None: #... - self.config = TechJSON( + self.config = TechConfig( #fields skipped... special_cells = [ SpecialCell(cell_type = CellType("tapcell"), name = ["sky130_fd_sc_hd__tapvpwrvgnd_1"]), @@ -318,7 +318,7 @@ The ``physical_only_cells_list`` is used to denote cells that contain only physi def gen_config(self) -> None: #... - self.config = TechJSON( + self.config = TechConfig( #fields skipped... physical_only_cells_list = [ "sky130_fd_sc_hd__tap_1", "sky130_fd_sc_hd__tap_2", "sky130_fd_sc_hd__tapvgnd_1", "sky130_fd_sc_hd__tapvpwrvgnd_1", diff --git a/hammer/tech/__init__.py b/hammer/tech/__init__.py index ec0ad7b61..71e2d99a2 100644 --- a/hammer/tech/__init__.py +++ b/hammer/tech/__init__.py @@ -211,7 +211,7 @@ def from_setting(grid_unit: Decimal, d: Dict[str, Any]) -> "Site": ) -class TechJSON(BaseModel): +class TechConfig(BaseModel): name: str grid_unit: Optional[str] = None shrink_factor: Optional[str] = None @@ -419,14 +419,14 @@ def __init__(self): self.package: str = "" # Configuration, since this constructor will never be used, self.config will never seen as None - self.config: TechJSON = None # type: ignore + self.config: TechConfig = None # type: ignore # Units (converted to Time/CapacitanceValue later) self.time_unit: Optional[str] = None self.cap_unit: Optional[str] = None def gen_config(self) -> None: - """For subclasses to set self.config (type: TechJSON) directly, instead of from static JSON file""" + """For subclasses to set self.config (type: TechConfig) directly, instead of from static JSON file""" pass @classmethod @@ -446,10 +446,10 @@ def load_from_module(cls, tech_module: str) -> "HammerTechnology": tech_yaml = importlib.resources.files(tech_module) / f"{technology_name}.tech.yml" if tech_json.is_file(): - tech.config = TechJSON.model_validate_json(tech_json.read_text()) + tech.config = TechConfig.model_validate_json(tech_json.read_text()) return tech elif tech_yaml.is_file(): - tech.config = TechJSON.model_validate_json(json.dumps(load_yaml(tech_yaml.read_text()))) + tech.config = TechConfig.model_validate_json(json.dumps(load_yaml(tech_yaml.read_text()))) return tech else: # Assume tech implents gen_config() return tech diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 7b91594c0..ecf0cb345 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -25,7 +25,7 @@ Site, Stackup, Supplies, - TechJSON, + TechConfig, ) from hammer.tech.specialcells import CellType, SpecialCell from hammer.utils import LEFUtils @@ -450,8 +450,7 @@ def gen_config(self) -> None: ) libs.append(lib_entry) - # TODO rename this to TechConfig or something, since we don';t use json - self.config = TechJSON( + self.config = TechConfig( name="Skywater 130nm Library", grid_unit="0.001", shrink_factor=None, From 1e99719a318d678d82a0a1c38d3e07fa5e7b0de4 Mon Sep 17 00:00:00 2001 From: elamdf Date: Wed, 19 Feb 2025 13:29:46 -0800 Subject: [PATCH 63/68] format + remove old todos --- hammer/technology/sky130/__init__.py | 161 +++++++++++---------------- 1 file changed, 64 insertions(+), 97 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index ecf0cb345..17d288d83 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -71,16 +71,13 @@ def gen_config(self) -> None: elif slib == "sky130_scl": libs += [ Library( - lef_file=os.path.join( - SKY130_SCL, "/lef/sky130_scl_9T.tlef"), - verilog_sim=os.path.join( - SKY130_SCL, "/verilog/sky130_scl_9T.v"), + lef_file=os.path.join(SKY130_SCL, "/lef/sky130_scl_9T.tlef"), + verilog_sim=os.path.join(SKY130_SCL, "/verilog/sky130_scl_9T.v"), provides=[Provide(lib_type="technology")], ), ] else: - raise ValueError( - f"Incorrect standard cell library selection: {slib}") + raise ValueError(f"Incorrect standard cell library selection: {slib}") # Stdcell library-dependent lists stackups = [] # type: List[Stackup] @@ -169,8 +166,7 @@ def gen_config(self) -> None: # scl vs 130a have different site names sites = None - STDCELL_LIBRARY_BASE_PATH = os.path.join( - SKY130A, "libs.ref", library) + STDCELL_LIBRARY_BASE_PATH = os.path.join(SKY130A, "libs.ref", library) lib_corner_files[STDCELL_LIBRARY_BASE_PATH] = os.listdir( os.path.join(STDCELL_LIBRARY_BASE_PATH, "lib") ) @@ -181,8 +177,7 @@ def gen_config(self) -> None: SKY130A, "libs.ref", library, "techlef", f"{library}__min.tlef" ) metals = list( - map(lambda m: Metal.model_validate(m), - LEFUtils.get_metals(tlef_path)) + map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path)) ) stackups.append( Stackup(name=slib, grid_unit=Decimal("0.001"), metals=metals) @@ -264,8 +259,7 @@ def gen_config(self) -> None: tlef_path = os.path.join(SKY130_SCL, "lef", f"{slib}_9T.tlef") metals = list( - map(lambda m: Metal.model_validate(m), - LEFUtils.get_metals(tlef_path)) + map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path)) ) stackups.append( Stackup(name=slib, grid_unit=Decimal("0.001"), metals=metals) @@ -274,16 +268,14 @@ def gen_config(self) -> None: sites = [ Site(name="CoreSite", x=Decimal("0.46"), y=Decimal("4.14")), Site(name="IOSite", x=Decimal("1.0"), y=Decimal("240.0")), - Site(name="CornerSite", x=Decimal( - "240.0"), y=Decimal("240.0")), + Site(name="CornerSite", x=Decimal("240.0"), y=Decimal("240.0")), ] lvs_decks = [ LVSDeck( tool_name="pegasus", deck_name="pegasus_lvs", - path=os.path.join( - SKY130_CDS, "Sky130_LVS", "sky130.lvs.pvl"), + path=os.path.join(SKY130_CDS, "Sky130_LVS", "sky130.lvs.pvl"), ) ] drc_decks = [ @@ -302,12 +294,10 @@ def gen_config(self) -> None: ] else: - raise ValueError( - f"Incorrect standard cell library selection: {slib}") + raise ValueError(f"Incorrect standard cell library selection: {slib}") # add skywater io cells - io_library_base_path = os.path.join( - SKY130A, "libs.ref", "sky130_fd_io") + io_library_base_path = os.path.join(SKY130A, "libs.ref", "sky130_fd_io") if os.path.exists(io_library_base_path): lib_corner_files[io_library_base_path] = os.listdir( os.path.join(io_library_base_path, "lib") @@ -340,14 +330,12 @@ def gen_config(self) -> None: if len(process) > 3: if not functools.reduce( lambda x, y: x and y, - map(lambda p, q: p == q, - process[0:3], process[4:]), + map(lambda p, q: p == q, process[0:3], process[4:]), True, ): continue # Determine actual corner - speed = next( - c for c in process if c is not None).replace("_", "") + speed = next(c for c in process if c is not None).replace("_", "") temp = temp_volt[0] temp = temp.replace("n", "-") temp = temp.split("C")[0] + " C" @@ -361,8 +349,7 @@ def gen_config(self) -> None: speed = "slow" else: self.logger.info( - "Skipping lib with unsupported corner: {}".format( - speed) + "Skipping lib with unsupported corner: {}".format(speed) ) continue else: @@ -383,8 +370,7 @@ def gen_config(self) -> None: speed = "slow" temp = "100 C" - cdl_path = os.path.join( - library_base_path, "cdl", library + ".cdl") + cdl_path = os.path.join(library_base_path, "cdl", library + ".cdl") spice_path = os.path.join( library_base_path, "spice", library + ".spice" ) @@ -392,8 +378,7 @@ def gen_config(self) -> None: # just prioritize spice, arbitrary choice # assert not (os.path.exists(cdl_path) and os.path.exists(spice_path)), "both spice and cdl netlists exist! this is ambiguous :(" - netlist_path = spice_path if os.path.exists( - spice_path) else cdl_path + netlist_path = spice_path if os.path.exists(spice_path) else cdl_path lib_entry = Library( nldm_liberty_file=os.path.join( @@ -404,11 +389,9 @@ def gen_config(self) -> None: "verilog", library + "_9T.v" if slib == "sky130_scl" else ".v", ), - lef_file=os.path.join( - library_base_path, "lef", library + ".lef"), + lef_file=os.path.join(library_base_path, "lef", library + ".lef"), spice_file=netlist_path, - gds_file=os.path.join( - library_base_path, "gds", library + ".gds"), + gds_file=os.path.join(library_base_path, "gds", library + ".gds"), corner=Corner(nmos=speed, pmos=speed, temperature=temp), supplies=Supplies(VDD=vdd, GND="0 V"), provides=[Provide(lib_type="stdcell", vt="RVT")], @@ -443,8 +426,7 @@ def gen_config(self) -> None: gds_file=os.path.join( library_base_path, "gds", extra_gds_file ), - corner=Corner(nmos=speed, pmos=speed, - temperature=temp), + corner=Corner(nmos=speed, pmos=speed, temperature=temp), supplies=Supplies(VDD=vdd, GND="0 V"), provides=[Provide(lib_type="stdcell", vt="RVT")], ) @@ -455,13 +437,10 @@ def gen_config(self) -> None: grid_unit="0.001", shrink_factor=None, installs=[ - PathPrefix(id="$SKY130_NDA", - path="technology.sky130.sky130_nda"), + PathPrefix(id="$SKY130_NDA", path="technology.sky130.sky130_nda"), PathPrefix(id="$SKY130A", path="technology.sky130.sky130A"), - PathPrefix(id="$SKY130_CDS", - path="technology.sky130.sky130_cds"), - PathPrefix(id="$SKY130_SCL", - path="technology.sky130.sky130_scl"), + PathPrefix(id="$SKY130_CDS", path="technology.sky130.sky130_cds"), + PathPrefix(id="$SKY130_SCL", path="technology.sky130.sky130_scl"), ], libraries=libs, gds_map_file="sky130_lefpin.map", @@ -509,18 +488,17 @@ def setup_calibre_lvs_deck(self) -> bool: source_path = Path(source_paths[i]) except IndexError: self.logger.error( - "No corresponding source for LVS deck {}".format(deck)) + "No corresponding source for LVS deck {}".format(deck) + ) continue if not source_path.exists(): raise FileNotFoundError(f"LVS deck not found: {source_path}") cache_tech_dir_path = Path(self.cache_dir) - dest_path = os.path.join(cache_tech_dir_path, - os.path.basename(deck.path)) + dest_path = os.path.join(cache_tech_dir_path, os.path.basename(deck.path)) with open(source_path, "r") as sf: with open(dest_path, "w") as df: self.logger.info( - "Modifying LVS deck: {} -> {}".format( - source_path, dest_path) + "Modifying LVS deck: {} -> {}".format(source_path, dest_path) ) df.write(matcher.sub("", sf.read())) df.write(LVS_DECK_INSERT_LINES) @@ -563,8 +541,7 @@ def setup_cdl(self) -> None: 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) + "Modifying CDL netlist: {} -> {}".format(source_path, dest_path) ) df.write("*.SCALE MICRON\n") for line in sf: @@ -599,8 +576,7 @@ def setup_verilog(self) -> None: 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) + "Modifying Verilog netlist: {} -> {}".format(source_path, dest_path) ) for line in sf: line = line.replace("wire 1", "// wire 1") @@ -624,8 +600,7 @@ def setup_verilog(self) -> None: 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) + "Modifying Verilog netlist: {} -> {}".format(source_path, dest_path) ) for line in sf: line = line.replace( @@ -659,8 +634,7 @@ def setup_techlef(self) -> None: 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) + "Modifying Technology LEF: {} -> {}".format(source_path, dest_path) ) for line in sf: df.write(line) @@ -669,15 +643,11 @@ def setup_techlef(self) -> None: == "sky130_scl" ): if line.strip() == "END poly": - df.write(_additional_tlef_edit_for_scl + - _the_tlef_edit) + df.write(_additional_tlef_edit_for_scl + _the_tlef_edit) else: if line.strip() == "END pwell": df.write(_the_tlef_edit) - # syn_power seems to not take hooks from `get_tech_syn_hooks` - # also, syn_power is called from joules so we need to add the hook to joules - # TODO: clean this up, there should be a way to always include this hook whenever genus will be invoked def get_tech_power_hooks(self, tool_name: str) -> List[HammerToolHookAction]: hooks = {} @@ -738,15 +708,13 @@ def setup_io_lefs(self) -> None: ) sl = sf.readlines() for net in ["VCCD1", "VSSD1", "VDDA", "VSSA", "VSSIO"]: - start = [idx for idx, line in enumerate( - sl) if "PIN " + net in line] - end = [idx for idx, line in enumerate( - sl) if "END " + net in line] + start = [idx for idx, line in enumerate(sl) if "PIN " + net in line] + end = [idx for idx, line in enumerate(sl) if "END " + net in line] intervals = zip(start, end) for intv in intervals: port_idx = [ idx - for idx, line in enumerate(sl[intv[0]: intv[1]]) + for idx, line in enumerate(sl[intv[0] : intv[1]]) if "PORT" in line and "met3" in sl[intv[0] + idx + 1] ] for idx in port_idx: @@ -778,8 +746,14 @@ def setup_hvl_ls_lef(self) -> bool: lef_name = "sky130_fd_sc_hvl__lsbufhv2lv_1.lef" - sky130A_path = Path(self.get_setting('technology.sky130.sky130A')) - source_path = sky130A_path / 'libs.ref' / 'sky130_fd_sc_hvl' / 'lef' / "sky130_fd_sc_hvl.lef" + sky130A_path = Path(self.get_setting("technology.sky130.sky130A")) + source_path = ( + sky130A_path + / "libs.ref" + / "sky130_fd_sc_hvl" + / "lef" + / "sky130_fd_sc_hvl.lef" + ) cache_path = Path(self.cache_dir) / "fd_sc_hvl__lef" / lef_name cache_path.parent.mkdir(exist_ok=True) @@ -789,17 +763,21 @@ def setup_hvl_ls_lef(self) -> bool: is_in_macro_def = False for line in sf: if is_in_site_def: - if 'END unithv' in line: + if "END unithv" in line: is_in_site_def = False - elif not is_in_macro_def and 'SITE unithv' in line: + elif not is_in_macro_def and "SITE unithv" in line: is_in_site_def = True - elif 'MACRO ' in line: + elif "MACRO " in line: is_in_macro_def = True df.write(line) - elif 'SITE unithv' in line: + elif "SITE unithv" in line: pass else: - df.write(line.replace("CLASS CORE", "CLASS BLOCK") if not (("ANTENNACELL" in line) or ("SPACER" in line)) else line) + df.write( + line.replace("CLASS CORE", "CLASS BLOCK") + if not (("ANTENNACELL" in line) or ("SPACER" in line)) + else line + ) def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]: hooks = { @@ -807,8 +785,7 @@ def get_tech_par_hooks(self, tool_name: str) -> List[HammerToolHookAction]: HammerTool.make_post_insertion_hook( "init_design", sky130_innovus_settings ), - HammerTool.make_pre_insertion_hook( - "power_straps", sky130_connect_nets), + HammerTool.make_pre_insertion_hook("power_straps", sky130_connect_nets), HammerTool.make_pre_insertion_hook( "write_design", sky130_connect_nets2 ), @@ -972,10 +949,8 @@ def sky130_sram_names() -> List[str]: # various Innovus database settings def sky130_innovus_settings(ht: HammerTool) -> bool: - assert isinstance( - ht, HammerPlaceAndRouteTool), "Innovus settings only for par" - assert isinstance( - ht, TCLTool), "innovus settings can only run on TCL tools" + assert isinstance(ht, HammerPlaceAndRouteTool), "Innovus settings only for par" + assert isinstance(ht, TCLTool), "innovus settings can only run on TCL tools" """Settings for every tool invocation""" ht.append( f""" @@ -1042,7 +1017,7 @@ def sky130_innovus_settings(ht: HammerTool) -> bool: ht.append( """ # note this is required for sky130_fd_sc_hd, the design has a ton of drcs if bottom layer is 1 - # TODO: why is setting routing_layer not enough? +# TODO: why is setting routing_layer not enough? set_db design_bottom_routing_layer 2 set_db design_top_routing_layer 6 # deprected syntax, but this used to always work @@ -1054,10 +1029,8 @@ def sky130_innovus_settings(ht: HammerTool) -> bool: 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" + 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( @@ -1082,10 +1055,8 @@ def sky130_connect_nets2(ht: HammerTool) -> bool: def sky130_add_endcaps(ht: HammerTool) -> bool: - assert isinstance( - ht, HammerPlaceAndRouteTool), "endcap insertion only for par" - assert isinstance( - ht, TCLTool), "endcap insertion can only run on TCL tools" + assert isinstance(ht, HammerPlaceAndRouteTool), "endcap insertion only for par" + assert isinstance(ht, TCLTool), "endcap insertion can only run on TCL tools" endcap_cells = ht.technology.get_special_cell_by_type(CellType.EndCap) endcap_cell = endcap_cells[0].name[0] ht.append( @@ -1110,9 +1081,10 @@ def set_cts_base_cells(ht: HammerTool) -> bool: return True -# TODO: this should just be placign rail straps, so higher straps are placed by non-hardocded tcl def power_rail_straps_no_tapcells(ht: HammerTool) -> bool: - assert not ht.get_setting("par.generate_power_straps_options.by_tracks.generate_rail_layer"), "rails must be placed by this hook for sky130_scl!" + assert not ht.get_setting( + "par.generate_power_straps_options.by_tracks.generate_rail_layer" + ), "rails must be placed by this hook for sky130_scl!" # We do this since there are no explicit tapcells in sky130_scl # just need the rail ones, others are placed as usual. ht.append( @@ -1128,7 +1100,6 @@ def power_rail_straps_no_tapcells(ht: HammerTool) -> bool: return True - def calibre_drc_blackbox_srams(ht: HammerTool) -> bool: assert isinstance(ht, HammerDRCTool), "Exlude SRAMs only in DRC" drc_box = "" @@ -1170,8 +1141,7 @@ def pegasus_drc_blackbox_srams(ht: HammerTool) -> bool: def calibre_lvs_blackbox_srams(ht: HammerTool) -> bool: - assert isinstance( - ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" + assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" lvs_box = "" for name in SKY130Tech.sky130_sram_names(): lvs_box += f"\nLVS BOX {name}" @@ -1185,8 +1155,7 @@ def calibre_lvs_blackbox_srams(ht: HammerTool) -> bool: # required for sram22 since they use the 130a primiviites def pegasus_lvs_add_130a_primitives(ht: HammerTool) -> bool: return True - assert isinstance( - ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" + assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" lvs_box = "" for name in SKY130Tech.sky130_sram_primitive_names(): lvs_box += f"""\nschematic_path "{name}" spice;""" @@ -1207,8 +1176,7 @@ def pegasus_lvs_add_130a_primitives(ht: HammerTool) -> bool: def pegasus_lvs_blackbox_srams(ht: HammerTool) -> bool: - assert isinstance( - ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" + assert isinstance(ht, HammerLVSTool), "Blackbox and filter SRAMs only in LVS" lvs_box = "" for ( name @@ -1239,5 +1207,4 @@ def sram22_lvs_recognize_gates_all(ht: HammerTool) -> bool: return True - tech = SKY130Tech() From e77a44fa68d9d12032999b7488ba983ce7c0b445 Mon Sep 17 00:00:00 2001 From: elamdf Date: Fri, 21 Feb 2025 07:21:06 -0800 Subject: [PATCH 64/68] formatting --- hammer/technology/sky130/__init__.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 17d288d83..0e6295ac7 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -1014,16 +1014,16 @@ def sky130_innovus_settings(ht: HammerTool) -> bool: """ ) - ht.append( - """ -# note this is required for sky130_fd_sc_hd, the design has a ton of drcs if bottom layer is 1 -# TODO: why is setting routing_layer not enough? -set_db design_bottom_routing_layer 2 -set_db design_top_routing_layer 6 -# deprected syntax, but this used to always work -set_db route_design_bottom_routing_layer 2 - """ - ) + # ht.append( + # """ + # # note this is required for sky130_fd_sc_hd, the design has a ton of drcs if bottom layer is 1 + # # TODO: why is setting routing_layer not enough? + # set_db design_bottom_routing_layer 2 + # set_db design_top_routing_layer 6 + # # deprected syntax, but this used to always work + # set_db route_design_bottom_routing_layer 2 + # """ + # ) return True @@ -1084,7 +1084,9 @@ def set_cts_base_cells(ht: HammerTool) -> bool: def power_rail_straps_no_tapcells(ht: HammerTool) -> bool: assert not ht.get_setting( "par.generate_power_straps_options.by_tracks.generate_rail_layer" - ), "rails must be placed by this hook for sky130_scl!" + ), """ +Rails must be placed by this hook for sky130_scl! +Set par.generate_power_straps_options.by_tracks.generate_rail_layer: false""" # We do this since there are no explicit tapcells in sky130_scl # just need the rail ones, others are placed as usual. ht.append( From b2493dfff6f3aa659b135a5c7c995afe7cea4c5f Mon Sep 17 00:00:00 2001 From: elamdf Date: Fri, 21 Feb 2025 07:21:12 -0800 Subject: [PATCH 65/68] include sram22 spice file as a library --- hammer/technology/sky130/__init__.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 0e6295ac7..695124afb 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -53,6 +53,10 @@ def gen_config(self) -> None: SKY130_CDS = self.get_setting("technology.sky130.sky130_cds") SKY130_SCL = self.get_setting("technology.sky130.sky130_scl") + self.use_sram22 = os.path.exists( + self.get_setting("technology.sky130.sram22_sky130_macros") + ) + # Common tech LEF and IO cell spice netlists libs = [] if slib == "sky130_fd_sc_hd": @@ -78,6 +82,16 @@ def gen_config(self) -> None: ] else: raise ValueError(f"Incorrect standard cell library selection: {slib}") + if self.use_sram22: + libs += [ + Library( + spice_file=os.path.join( + self.get_setting("technology.sky130.sram22_sky130_macros"), + "sram22.spice", + ), + provides=[Provide(lib_type="technology")], + ), + ] # Stdcell library-dependent lists stackups = [] # type: List[Stackup] @@ -460,9 +474,6 @@ def gen_config(self) -> None: 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_sram22 = os.path.exists( - self.get_setting("technology.sky130.sram22_sky130_macros") - ) if self.get_setting("technology.sky130.stdcell_library") == "sky130_fd_sc_hd": self.setup_cdl() self.setup_verilog() From 643823d68be248c62bf5d72a603c0f12f567483b Mon Sep 17 00:00:00 2001 From: Elam Date: Fri, 16 May 2025 10:37:49 -0700 Subject: [PATCH 66/68] fix: update paths for new Cadence directory structure in v0.0.6 --- hammer/technology/sky130/__init__.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index 695124afb..ea997c1ba 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -75,8 +75,8 @@ def gen_config(self) -> None: elif slib == "sky130_scl": libs += [ Library( - lef_file=os.path.join(SKY130_SCL, "/lef/sky130_scl_9T.tlef"), - verilog_sim=os.path.join(SKY130_SCL, "/verilog/sky130_scl_9T.v"), + lef_file=os.path.join(SKY130_SCL, "sky130_scl_9T_tech/lefn/sky130_scl_9T.tlef"), + verilog_sim=os.path.join(SKY130_SCL, "sky130_scl_9T/verilog/sky130_scl_9T.v"), provides=[Provide(lib_type="technology")], ), ] @@ -262,7 +262,7 @@ def gen_config(self) -> None: # Generate standard cell library library = slib - STDCELL_LIBRARY_BASE_PATH = SKY130_SCL + STDCELL_LIBRARY_BASE_PATH = os.path.join(SKY130_SCL, "sky130_scl_9T") lib_corner_files[STDCELL_LIBRARY_BASE_PATH] = os.listdir( os.path.join(STDCELL_LIBRARY_BASE_PATH, "lib") ) @@ -271,7 +271,7 @@ def gen_config(self) -> None: # Generate stackup metals = [] # type: List[Metal] - tlef_path = os.path.join(SKY130_SCL, "lef", f"{slib}_9T.tlef") + tlef_path = os.path.join(SKY130_SCL, "sky130_scl_9T_tech", "lef", f"{slib}_9T.tlef") metals = list( map(lambda m: Metal.model_validate(m), LEFUtils.get_metals(tlef_path)) ) @@ -368,7 +368,7 @@ def gen_config(self) -> None: continue else: library = "sky130_scl_9T" - _, speed, vdd, temp = tmp.split("_") + _, speed, vdd, temp, _ = tmp.split("_") # force equivalent operating conditions for speed, since they're different between sky130a and scl if speed == "ff": @@ -400,6 +400,7 @@ def gen_config(self) -> None: ), verilog_sim=os.path.join( SKY130_SCL, + "sky130_scl_9T", "verilog", library + "_9T.v" if slib == "sky130_scl" else ".v", ), @@ -430,6 +431,7 @@ def gen_config(self) -> None: ), verilog_sim=os.path.join( SKY130_SCL, + "sky130_scl_9T", "verilog", library + "_9T.v" if slib == "sky130_scl" else ".v", ), @@ -637,7 +639,7 @@ def setup_techlef(self) -> None: else: setting_dir = self.get_setting("technology.sky130.sky130_scl") setting_dir = Path(setting_dir) - source_path = setting_dir / "lef" / "sky130_scl_9T.tlef" + source_path = setting_dir / "sky130_scl_9T_tech" / "lef" / "sky130_scl_9T.tlef" dest_path = cache_tech_dir_path / "sky130_scl_9T.tlef" if not source_path.exists(): raise FileNotFoundError(f"Tech-LEF not found: {source_path}") From b12fca62da3f370a09d284ac3a603da409190cf7 Mon Sep 17 00:00:00 2001 From: Elam Date: Fri, 16 May 2025 12:20:58 -0700 Subject: [PATCH 67/68] fix: update deck names --- hammer/technology/sky130/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index ea997c1ba..f76be96cb 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -227,7 +227,7 @@ def gen_config(self) -> None: DRCDeck( tool_name="pegasus", deck_name="pegasus_drc", - path="$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_1.0.drc.pvl", + path="$SKY130_CDS/Sky130_DRC/sky130_rev_0.0_2.2.drc.pvl", ), ] @@ -302,7 +302,7 @@ def gen_config(self) -> None: tool_name="pegasus", deck_name="pegasus_drc", path=os.path.join( - SKY130_CDS, "Sky130_DRC", "sky130_rev_0.0_1.0.drc.pvl" + SKY130_CDS, "Sky130_DRC", "sky130_rev_0.0_2.2.drc.pvl" ), ), ] From 518ee4e36c5ed6718b32c1c0c2ab9915268dd692 Mon Sep 17 00:00:00 2001 From: Elam Date: Fri, 16 May 2025 15:24:15 -0700 Subject: [PATCH 68/68] add phycells lef, fix spelling --- hammer/technology/sky130/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hammer/technology/sky130/__init__.py b/hammer/technology/sky130/__init__.py index f76be96cb..11dec55cb 100644 --- a/hammer/technology/sky130/__init__.py +++ b/hammer/technology/sky130/__init__.py @@ -75,11 +75,17 @@ def gen_config(self) -> None: elif slib == "sky130_scl": libs += [ Library( - lef_file=os.path.join(SKY130_SCL, "sky130_scl_9T_tech/lefn/sky130_scl_9T.tlef"), + lef_file=os.path.join(SKY130_SCL, "sky130_scl_9T_tech/lef/sky130_scl_9T.tlef"), verilog_sim=os.path.join(SKY130_SCL, "sky130_scl_9T/verilog/sky130_scl_9T.v"), provides=[Provide(lib_type="technology")], ), ] + libs += [ + Library( + lef_file=os.path.join(SKY130_SCL, "sky130_scl_9T_tech/lef/sky130_scl_9T_phyCells.lef"), + provides=[Provide(lib_type="technology")], + ), + ] else: raise ValueError(f"Incorrect standard cell library selection: {slib}") if self.use_sram22: