From cb7f117daa04123f04e4c8cb5f16219a13f01e08 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 22 Apr 2026 01:33:47 -0700 Subject: [PATCH] squash commits --- compiler/drc/custom_cell_properties.py | 4 +- compiler/modules/bitcell_array.py | 4 +- compiler/modules/bitcell_base.py | 1 + compiler/modules/bitcell_base_array.py | 7 +- .../modules/capped_replica_bitcell_array.py | 25 +- compiler/modules/col_cap_array.py | 2 +- compiler/modules/dummy_array.py | 4 +- compiler/modules/pattern.py | 2 +- compiler/modules/replica_bitcell_array.py | 12 +- compiler/modules/replica_column.py | 8 +- compiler/modules/row_cap_array.py | 8 +- macros/sram_configs/sky130_sram_1rw_tiny.py | 2 +- .../custom/sky130_bitcell_base_array.py | 4 +- .../sky130/custom/sky130_replica_column.py | 4 +- .../sky130/custom/sky130_row_cap_array.py | 13 +- technology/sky130/tech/tech.py | 2 +- .../tech/tech_configs/tech_custom_cell.py | 857 ++++++++++++++++++ .../tech/tech_configs/tech_cypress_cell.py | 848 +++++++++++++++++ 18 files changed, 1768 insertions(+), 39 deletions(-) create mode 100644 technology/sky130/tech/tech_configs/tech_custom_cell.py create mode 100644 technology/sky130/tech/tech_configs/tech_cypress_cell.py diff --git a/compiler/drc/custom_cell_properties.py b/compiler/drc/custom_cell_properties.py index d7972be1..6c5a7240 100644 --- a/compiler/drc/custom_cell_properties.py +++ b/compiler/drc/custom_cell_properties.py @@ -144,11 +144,11 @@ class _pgate: class bitcell(cell): - def __init__(self, port_order, port_types, port_map=None, storage_nets=["Q", "Q_bar"], mirror=None, end_caps=False): + def __init__(self, port_order, port_types, port_map=None, storage_nets=["Q", "Q_bar"], mirror=None, end_caps=False, has_corners=True): super().__init__(port_order, port_types, port_map) self.end_caps = end_caps - + self.has_corners = has_corners if not mirror: self.mirror = _mirror_axis(True, False) else: diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 264f9484..e2292df5 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -72,8 +72,8 @@ class bitcell_array(bitcell_base_array): core_block = [[0 for x in range(1)] for y in range(2)] core_block[(0 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) core_block[(1 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') - print(r, c) - print(core_block) + #print(r, c) + #print(core_block) self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.row_size, num_cols=self.column_size,name_template="bit_r{0}_c{1}") self.pattern.connect_array() diff --git a/compiler/modules/bitcell_base.py b/compiler/modules/bitcell_base.py index 3fcdd476..f2f03217 100644 --- a/compiler/modules/bitcell_base.py +++ b/compiler/modules/bitcell_base.py @@ -25,6 +25,7 @@ class bitcell_base(design): self.nets_match = self.do_nets_exist(prop.storage_nets) self.mirror = prop.mirror self.end_caps = prop.end_caps + self.has_corners = prop.has_corners def get_stage_effort(self, load): parasitic_delay = 1 # This accounts for bitline being drained diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 8c6a813a..f470bc46 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -121,13 +121,14 @@ class bitcell_base_array(design): def get_all_wordline_names(self, port=None): """ Return all the wordline names """ temp = [] - temp.extend(self.get_rbl_wordline_names(0)) + if len(self.all_ports) > 1: + temp.extend(self.get_rbl_wordline_names(1)) if port == None: temp.extend(self.all_wordline_names) else: temp.extend(self.wordline_names[port]) - if len(self.all_ports) > 1: - temp.extend(self.get_rbl_wordline_names(1)) + temp.extend(self.get_rbl_wordline_names(0)) + return temp def add_bitline_pins(self): diff --git a/compiler/modules/capped_replica_bitcell_array.py b/compiler/modules/capped_replica_bitcell_array.py index eb0c68e1..c255f0d3 100644 --- a/compiler/modules/capped_replica_bitcell_array.py +++ b/compiler/modules/capped_replica_bitcell_array.py @@ -86,7 +86,7 @@ class capped_replica_bitcell_array(bitcell_base_array): rows=1, # dummy column + left replica column(s) column_offset=1, - row_offset=self.row_size+ self.extra_rows, + row_offset=self.row_size+ self.extra_rows + 1, #add 1 to account for bottom col_cap mirror=0, location="top", left_rbl=self.left_rbl, @@ -162,10 +162,21 @@ class capped_replica_bitcell_array(bitcell_base_array): self.unused_wordline_names = self.replica_bitcell_array.unused_wordline_names self.replica_array_wordline_names_with_grounded_wls = ["gnd" if x in self.unused_wordline_names else x for x in self.replica_bitcell_array.wordline_pin_list] + # Left/right row caps cover the full array height. Pad with gnd so the + # netlist list length matches the row cap (replica in the center); do + # not use col cap wordline heuristics. + + n_rowcap_wl = len(self.row_cap_left.get_wordline_names()) + n_rba_wl = len(self.replica_array_wordline_names_with_grounded_wls) + + self.wordline_pin_list = [] - self.wordline_pin_list.extend(["gnd"] * len(self.col_cap_top.get_wordline_names())) + + if self.rbls: + self.wordline_pin_list.extend(["gnd"] * len(self.rbls)) self.wordline_pin_list.extend(self.replica_array_wordline_names_with_grounded_wls) - self.wordline_pin_list.extend(["gnd"] * len(self.col_cap_bottom.get_wordline_names())) + if self.rbls: + self.wordline_pin_list.extend(["gnd"] * len(self.rbls)) self.add_pin_list(self.used_wordline_names, "INPUT") @@ -192,6 +203,10 @@ class capped_replica_bitcell_array(bitcell_base_array): self.dummy_col_insts.append(self.add_inst(name="dummy_col_left", mod=self.row_cap_left)) self.connect_inst(["dummy_left_" + bl for bl in self.row_cap_left.all_bitline_names] + self.wordline_pin_list + self.supplies) + + #print(self.dummy_col_insts[0].mod.pins) + #print(["dummy_left_" + bl for bl in self.row_cap_left.all_bitline_names] + self.wordline_pin_list + self.supplies) + self.dummy_col_insts.append(self.add_inst(name="dummy_col_right", mod=self.row_cap_right)) self.connect_inst(["dummy_right_" + bl for bl in self.row_cap_right.all_bitline_names] + self.wordline_pin_list + self.supplies) @@ -282,10 +297,14 @@ class capped_replica_bitcell_array(bitcell_base_array): # Far left dummy col dummy_col_width = vector(self.dummy_col_insts[0].width, 0) offset = self.dummy_row_insts[0].ll() - dummy_col_width + if self.dummy_col_insts[0].mod.cell.has_corners is False: + offset += vector(0, dummy_row_height.y) self.dummy_col_insts[0].place(offset=offset) # Far right dummy col offset = self.dummy_row_insts[0].lr() + if self.dummy_col_insts[0].mod.cell.has_corners is False: + offset += vector(0, dummy_row_height.y) self.dummy_col_insts[1].place(offset=offset) def add_layout_pins(self): diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index 99940e98..dffe9459 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -15,7 +15,7 @@ class col_cap_array(bitcell_base_array): Generate a dummy row/column for the replica array. """ def __init__(self, rows, cols, column_offset=0, row_offset=0, mirror=0, location="", name="",left_rbl=[],right_rbl=[]): - super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) + super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name) self.mirror = mirror self.location = location diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 45b04c41..85970e6f 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -67,8 +67,8 @@ class dummy_array(bitcell_base_array): core_block = [[0 for x in range(1)] for y in range(2)] core_block[(0 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) core_block[(1 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - print(r, c) - print(core_block) + #print(r, c) + #print(core_block) self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size, name_template="bit_r{0}_c{1}") self.pattern.connect_array() diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index 72ef37de..9c52436d 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -182,7 +182,7 @@ class pattern(): y += inst.height if "Y" in inst.mirror: x += inst.width - print('placing inst {} at {}'.format(inst, offset)) + #print('placing inst {} at {}'.format(inst, offset)) inst.place((x, y), inst.mirror, inst.rotate) def place_array(self): diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index d27fd2df..e338a48b 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -76,8 +76,8 @@ class replica_bitcell_array(bitcell_base_array): """ Array and dummy/replica columns """ # Bitcell array self.bitcell_array = factory.create(module_type="bitcell_array", - column_offset=len(self.left_rbl), - row_offset=len(self.left_rbl), + column_offset=len(self.left_rbl)+ 1, #add 1 to account for left row_cap + row_offset=len(self.left_rbl)+1, #add 1 to account for bottom col_cap cols=self.column_size, rows=self.row_size, left_rbl=self.left_rbl, @@ -92,11 +92,11 @@ class replica_bitcell_array(bitcell_base_array): if port in self.left_rbl: # These go top down starting from the bottom of the bitcell array. replica_bit = self.rbl[0] - port - 1 - column_offset = 0 + column_offset = 1 elif port in self.right_rbl: # These go bottom up starting from the top of the bitcell array. replica_bit = self.rbl[0] + self.row_size + port - 1 - column_offset = len(self.left_rbl) + self.column_size + column_offset = len(self.left_rbl) + self.column_size + 1 else: continue @@ -120,8 +120,8 @@ class replica_bitcell_array(bitcell_base_array): self.dummy_rows[port] = factory.create(module_type="dummy_array", cols=self.column_size, rows=1, - row_offset=row_offset, - column_offset=len(self.left_rbl)) + row_offset=row_offset+1, #add 1 to account for bottom col_cap + column_offset=len(self.left_rbl)+1) #add 1 to account for left row_cap def add_pins(self): diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index c07b039c..855def17 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -96,14 +96,14 @@ class replica_column(bitcell_base_array): # All other cells are dummies if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end): if current_row % 2 == 0: - core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True) - else: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MX') + else: + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True) else: if current_row % 2 == 0: - core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True) - else: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MX') + else: + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True) current_row += 1 diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index 813869e3..c4ad50ce 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -15,7 +15,7 @@ class row_cap_array(bitcell_base_array): Generate a dummy row/column for the replica array. """ def __init__(self, rows, cols, column_offset=0, row_offset=0, mirror=0, location="", name=""): - super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) + super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name) self.mirror = mirror self.location = location self.row_offset = row_offset @@ -76,7 +76,11 @@ class row_cap_array(bitcell_base_array): pattern.append_row_to_block(bit_block, [rowend_m]) #pattern.append_row_to_block(bit_block, [bottom_corner]) - self.pattern = pattern(self, "row_cap_array_" + self.location, bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="row_cap_array" + self.location + "_r{0}_c{1}") + if self.cell.has_corners is False: + num_rows = self.row_size - 2 + else: + num_rows = self.row_size + self.pattern = pattern(self, "row_cap_array_" + self.location, bit_block, num_rows=num_rows, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="row_cap_array" + self.location + "_r{0}_c{1}") self.pattern.connect_array_raw() def get_bitcell_pins(self, row, col): diff --git a/macros/sram_configs/sky130_sram_1rw_tiny.py b/macros/sram_configs/sky130_sram_1rw_tiny.py index e04bb3a6..20aaa465 100644 --- a/macros/sram_configs/sky130_sram_1rw_tiny.py +++ b/macros/sram_configs/sky130_sram_1rw_tiny.py @@ -26,4 +26,4 @@ num_sim_threads = 1 #analytical_delay = False import os exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) -#tech_file = "tech_custom_cell" \ No newline at end of file +tech_file = "tech_custom_cell" \ No newline at end of file diff --git a/technology/sky130/custom/sky130_bitcell_base_array.py b/technology/sky130/custom/sky130_bitcell_base_array.py index 292ff39d..89eb2604 100644 --- a/technology/sky130/custom/sky130_bitcell_base_array.py +++ b/technology/sky130/custom/sky130_bitcell_base_array.py @@ -17,8 +17,8 @@ class sky130_bitcell_base_array(bitcell_base_array): """ Abstract base class for bitcell-arrays -- bitcell, dummy, replica """ - def __init__(self, name, rows, cols, column_offset): - super().__init__(name, rows, cols, column_offset) + def __init__(self, name, rows, cols, column_offset, row_offset): + super().__init__(name, rows, cols, column_offset, row_offset) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") diff --git a/technology/sky130/custom/sky130_replica_column.py b/technology/sky130/custom/sky130_replica_column.py index a40e23fa..f83b504d 100644 --- a/technology/sky130/custom/sky130_replica_column.py +++ b/technology/sky130/custom/sky130_replica_column.py @@ -80,12 +80,12 @@ class sky130_replica_column(replica_column, sky130_bitcell_base_array): # Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell. # All other cells are dummies if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end): - if current_row % 2 == 1: + if current_row % 2 == 0: pattern.append_row_to_block(bit_block, replica_row_opt1) else: pattern.append_row_to_block(bit_block, replica_row_opt1a) else: - if current_row % 2 == 1: + if current_row % 2 == 0: pattern.append_row_to_block(bit_block, dummy_row_opt1) else: pattern.append_row_to_block(bit_block, dummy_row_opt1a) diff --git a/technology/sky130/custom/sky130_row_cap_array.py b/technology/sky130/custom/sky130_row_cap_array.py index 92d97201..1130556b 100644 --- a/technology/sky130/custom/sky130_row_cap_array.py +++ b/technology/sky130/custom/sky130_row_cap_array.py @@ -56,13 +56,15 @@ class sky130_row_cap_array(row_cap_array, sky130_bitcell_base_array): rowend = geometry.instance("row_cap_rowend", mod=self.rowend, is_bitcell=True, mirror="MX") rowenda = geometry.instance("row_cap_rowenda", mod=self.rowenda, is_bitcell=True) - pattern.append_row_to_block(bit_block, [top_corner]) + pattern.append_row_to_block(bit_block, [bottom_corner]) for row in range(1, self.row_size-1): if row % 2 == 1: - pattern.append_row_to_block(bit_block, [rowend]) - else: pattern.append_row_to_block(bit_block, [rowenda]) - pattern.append_row_to_block(bit_block, [bottom_corner]) + + else: + pattern.append_row_to_block(bit_block, [rowend]) + + pattern.append_row_to_block(bit_block, [top_corner]) self.pattern = pattern(self, "row_cap_array_" + self.location, bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="row_cap_array" + self.location + "_r{0}_c{1}") self.pattern.connect_array_raw() @@ -77,8 +79,6 @@ class sky130_row_cap_array(row_cap_array, sky130_bitcell_base_array): bitcell_pins.append("vdd") # vdd bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))]) - #bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))]) - return bitcell_pins def get_strap_pins(self, row, col): @@ -94,7 +94,6 @@ class sky130_row_cap_array(row_cap_array, sky130_bitcell_base_array): def create_all_wordline_names(self, row_size=None, start_row=0): if row_size == None: row_size = self.row_size - row_size = row_size - 2 for row in range(start_row, row_size): for port in self.all_ports: self.wordline_names[port].append("wl_{0}_{1}".format(port, row)) diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index e65a13db..3aa4659f 100755 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -15,5 +15,5 @@ sys.path.append("{}/{}".format(dir_path,'tech_configs')) if not hasattr(OPTS, 'tech_file'): OPTS.tech_file = 'tech_cypress_cell' -#TODO: FIX THIS TERRIBLE HACK JUST FOR TESTING EXECUTING +#TODO: FIX THIS TERRIBLE HACK JUST FOR TESTING exec('from {} import *'.format(OPTS.tech_file)) diff --git a/technology/sky130/tech/tech_configs/tech_custom_cell.py b/technology/sky130/tech/tech_configs/tech_custom_cell.py new file mode 100644 index 00000000..b797cc64 --- /dev/null +++ b/technology/sky130/tech/tech_configs/tech_custom_cell.py @@ -0,0 +1,857 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California +# All rights reserved. +# + + +import os +from openram import drc as d + +""" +File containing the process technology parameters for Skywater 130nm. +""" + +################################################### +# Custom modules +################################################### + +# This uses the default classes to instantiate module from +# '$OPENRAM_HOME/compiler/modules'. +# Using tech_modules['cellname'] you can override each class by providing a custom +# implementation in '$OPENRAM_TECHDIR/modules/' +# For example: tech_modules["contact"] = "contact_freepdk45" +tech_modules = d.module_type() + + +# These modules have been hand designed and provided in this repository. +tech_modules["nand2_dec"] = "nand2_dec" +tech_modules["nand3_dec"] = "nand3_dec" +tech_modules["nand4_dec"] = "nand4_dec" + +# Override default OpenRAM modules to sky130 modules +# These are for single port and dual port as a list, +# or for both if there is no list, +# or only applicable to one if there is no list. + +#tech_modules["bitcell_1port"] = "sky130_bitcell" +#tech_modules["replica_bitcell_1port"] = "sky130_replica_bitcell" +#tech_modules["dummy_bitcell_1port"] = "sky130_dummy_bitcell" + +tech_modules["replica_bitcell_2port"] = "replica_bitcell_2port" +tech_modules["dummy_bitcell_2port"] = "dummy_bitcell_2port" +tech_modules["bitcell_2port"] = "bitcell_2port" + +tech_modules["bitcell_array"] = ["bitcell_array", "bitcell_array"] +tech_modules["replica_bitcell_array"] = ["replica_bitcell_array", "replica_bitcell_array"] +tech_modules["capped_replica_bitcell_array"] = ["capped_replica_bitcell_array", "capped_replica_bitcell_array"] +tech_modules["dummy_array"] = ["dummy_array", "dummy_array"] + +tech_modules["replica_column"] = ["replica_column", "replica_column"] + +tech_modules["col_cap_array"] = ["col_cap_array", "col_cap_array"] +tech_modules["col_cap"] = ["col_cap_bitcell_1port", "col_cap_bitcell_2port"] +tech_modules["corner"] = ["sky130_corner", None] +tech_modules["internal"] = ["sky130_internal", None] +tech_modules["row_cap_array"] = ["sky130_row_cap_array", "row_cap_array"] +tech_modules["row_cap"] = ["sky130_row_cap", "row_cap_bitcell_2port"] + +# These modules are auto-generated from the nand decoders above and are not +# found in this. +tech_modules["buf_dec"] = "pbuf_dec" +tech_modules["inv_dec"] = "pinv_dec" +tech_modules["and2_dec"] = "and2_dec" +tech_modules["and3_dec"] = "and3_dec" +tech_modules["and4_dec"] = "and4_dec" + +################################################### +# Custom cell properties +################################################### +cell_properties = d.cell_properties() + +cell_properties.power_name = 'VPWR' +cell_properties.ground_name = 'VGND' + +cell_properties.bitcell_power_pin_directions = ("V", "V") + +cell_properties.bitcell_1port.mirror.x = True +cell_properties.bitcell_1port.mirror.y = True +cell_properties.bitcell_1port.end_caps = False +cell_properties.bitcell_1port.has_corners = True + +cell_properties.bitcell_1port.boundary_layer = "boundary" +cell_properties.bitcell_1port.port_order = ['bl', 'br', 'wl', 'vdd', 'gnd'] +cell_properties.bitcell_1port.port_types = ["INPUT", "INPUT", "GROUND", "POWER", "OUTPUT"] +cell_properties.bitcell_1port.port_map = {'bl': 'BL', + 'br': 'BR', + 'gnd': 'VGND', + 'vdd': 'VPWR', + 'wl': 'WL'} + + + + +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.bl_layer = "m1" +cell_properties.bitcell_1port.vdd_layer = "m1" +cell_properties.bitcell_1port.vdd_dir = "V" +cell_properties.bitcell_1port.gnd_layer = "m1" +cell_properties.bitcell_1port.gnd_dir = "V" + +cell_properties.bitcell_2port.mirror.x = True +cell_properties.bitcell_2port.mirror.y = True +cell_properties.bitcell_2port.end_caps = True +cell_properties.bitcell_2port.has_corners = True +cell_properties.bitcell_2port.port_order = ['bl0', 'br0', 'bl1', 'br1', 'wl0', 'wl1', 'vdd', 'gnd'] +cell_properties.bitcell_2port.port_map = {'bl0': 'BL0', + 'br0': 'BR0', + 'bl1': 'BL1', + 'br1': 'BR1', + 'wl0': 'WL0', + 'wl1': 'WL1', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.vdd_layer = "m1" +cell_properties.bitcell_1port.vdd_dir = "V" +cell_properties.bitcell_1port.gnd_layer = "m1" +cell_properties.bitcell_1port.gnd_dir = "V" +cell_properties.bitcell_2port.wl_layer = "m2" +cell_properties.bitcell_2port.vdd_layer = "m1" +cell_properties.bitcell_2port.vdd_dir = "H" +cell_properties.bitcell_2port.gnd_layer = "m2" +cell_properties.bitcell_2port.gnd_dir = "H" + +cell_properties.col_cap_1port_bitcell = d.cell(['bl', 'br', 'vdd', 'gnd',], + ['INPUT', 'INPUT','POWER', 'GROUND', ], + {'bl': 'bl', + 'br': 'br', + 'vdd': 'vdd', + 'gnd': 'gnd',}) +cell_properties.col_cap_1port_bitcell.boundary_layer = "boundary" + +cell_properties.col_cap_1port_strap_power = d.cell(['vdd', 'vpb', 'vnb'], + ['POWER', 'BIAS', 'BIAS'], + {'vnb': 'VNB', + 'vpb': 'VPB', + 'vdd': 'VPWR'}) +cell_properties.col_cap_1port_strap_power.boundary_layer = "boundary" + +cell_properties.col_cap_1port_strap_ground = d.cell(['gnd', 'vpb', 'vnb'], + ['GROUND', 'BIAS', 'BIAS'], + {'vnb': 'VNB', + 'vpb': 'VPB', + 'gnd': 'VGND'}) +cell_properties.col_cap_1port_strap_ground.boundary_layer = "boundary" + +cell_properties.row_cap_1port_cell = d.cell(['vdd', 'wl'], + ['POWER', 'INPUT'], + {'wl': 'WL', + 'vdd': 'VPWR'}) +cell_properties.row_cap_1port_cell.boundary_layer = "boundary" + +cell_properties.col_cap_2port.port_order = ['bl0', 'br0', 'bl1', 'br1', 'vdd'] +cell_properties.col_cap_2port.port_map = {'bl0': 'BL0', + 'br0': 'BR0', + 'bl1': 'BL1', + 'br1': 'BR1', + 'vdd': 'VDD'} + +cell_properties.row_cap_2port.port_order = ['wl0', 'wl1', 'gnd'] +cell_properties.row_cap_2port.port_map = {'wl0': 'WL0', + 'wl1': 'WL1', + 'gnd': 'GND'} + + +cell_properties.ptx.bin_spice_models = True +cell_properties.ptx.model_is_subckt = True + +cell_properties.pgate.add_implants = True + +cell_properties.use_strap = False +cell_properties.strap_module = "internal" +cell_properties.strap_version = "wlstrap" + +cell_properties.dff.port_order = ['D', 'Q', 'clk', 'vdd', 'gnd'] +cell_properties.dff.port_map = {'D': 'D', + 'Q': 'Q', + 'clk': 'CLK', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.nand2_dec.port_order = ['A', 'B', 'Z', 'vdd', 'gnd'] +cell_properties.nand2_dec.port_map = {'A': 'A', + 'B': 'B', + 'Z': 'Z', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.nand3_dec.port_order = ['A', 'B', 'C', 'Z', 'vdd', 'gnd'] +cell_properties.nand3_dec.port_map = {'A': 'A', + 'B': 'B', + 'C': 'C', + 'Z': 'Z', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.nand4_dec.port_order = ['A', 'B', 'C', 'D', 'Z', 'vdd', 'gnd'] +cell_properties.nand4_dec.port_map = {'A': 'A', + 'B': 'B', + 'C': 'C', + 'D': 'D', + 'Z': 'Z', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.sense_amp.port_order = ['bl', 'br', 'dout', 'en', 'vdd', 'gnd'] +cell_properties.sense_amp.port_map = {'bl': 'BL', + 'br': 'BR', + 'dout': 'DOUT', + 'en': 'EN', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.write_driver.port_order = ['din', 'bl', 'br', 'en', 'vdd', 'gnd'] +cell_properties.write_driver.port_map = {'din': 'DIN', + 'bl': 'BL', + 'br': 'BR', + 'en': 'EN', + 'vdd': 'VDD', + 'gnd': 'GND'} + + +# You can override the GDS for custom cell using the following: +# If it is a list, the first is single port and the second is dual port. +# If it is string, it is used for both single and dual port. +cell_properties.names["dff"] = "sky130_fd_bd_sram__openram_dff" +cell_properties.names["nand2_dec"] = ["sky130_fd_bd_sram__openram_dp_nand2_dec", "sky130_fd_bd_sram__openram_dp_nand2_dec"] +cell_properties.names["nand3_dec"] = ["sky130_fd_bd_sram__openram_dp_nand3_dec", "sky130_fd_bd_sram__openram_dp_nand3_dec"] +cell_properties.names["nand4_dec"] = ["sky130_fd_bd_sram__openram_dp_nand4_dec", "sky130_fd_bd_sram__openram_dp_nand4_dec"] + +cell_properties.names["bitcell_1port"] = "sky130_custom_cell" +cell_properties.names["replica_bitcell_1port"] = "sky130_custom_replica" +cell_properties.names["dummy_bitcell_1port"] = "sky130_custom_dummy" + +cell_properties.names["bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell" +cell_properties.names["dummy_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_dummy" +cell_properties.names["replica_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_replica" +cell_properties.names["col_cap_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_cap_col" +cell_properties.names["row_cap_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_cap_row" +cell_properties.names["sense_amp"] = "sky130_fd_bd_sram__openram_sense_amp" +cell_properties.names["write_driver"] = "sky130_fd_bd_sram__openram_write_driver" + +array_row_multiple = 2 +array_col_multiple = 2 + +################################################### +# Custom layer properties +################################################### +layer_properties = d.layer_properties() +layer_properties.hierarchical_decoder.bus_layer = "m1" +layer_properties.hierarchical_decoder.bus_directions = "nonpref" +layer_properties.hierarchical_decoder.input_layer = "li" +layer_properties.hierarchical_decoder.output_layer = "m2" +layer_properties.hierarchical_decoder.vertical_supply = True + +layer_properties.hierarchical_predecode.bus_layer = "m1" +layer_properties.hierarchical_predecode.bus_directions = "nonpref" +# This is added to allow the column decoder connections on m2 +layer_properties.hierarchical_predecode.bus_pitch_factor = 1.2 +layer_properties.hierarchical_predecode.bus_space_factor = 1.5 +layer_properties.hierarchical_predecode.input_layer = "li" +layer_properties.hierarchical_predecode.output_layer = "m2" +layer_properties.hierarchical_predecode.vertical_supply = True +layer_properties.hierarchical_predecode.force_horizontal_input_contact = True + +layer_properties.bank.stack = "m2_stack" +layer_properties.bank.pitch = "m3_pitch" + +layer_properties.column_mux_array.select_layer = "m3" +layer_properties.column_mux_array.bitline_layer = "m1" + +layer_properties.port_address.supply_offset = True + +layer_properties.port_data.enable_layer = "m1" +layer_properties.port_data.channel_route_bitlines = False + +layer_properties.replica_column.even_rows = False + +layer_properties.wordline_driver.vertical_supply = True + +layer_properties.global_wordline_layer = "m5" + + +################################################### +# Discrete tx bins +################################################### +# enforce that tx sizes are within 25% of requested size after fingering. +accuracy_requirement = 0.75 +nmos_bins = { + 0.15 : [0.36, 0.39, 0.42, 0.52, 0.54, 0.55, 0.58, 0.6, 0.61, 0.64, 0.65, 0.74, 0.84, 1.0, 1.26, 1.68, 2.0, 3.0, 5.0, 7.0], + 0.18 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 0.25 : [0.65, 1.0, 3.0, 5.0, 7.0], + 0.5 : [0.42, 0.55, 0.65, 1.0, 3.0, 5.0, 7.0], + 1.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 2.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 4.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 8.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 20.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0] +} + +pmos_bins = { + 0.15 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12, 1.26, 1.65, 1.68, 2.0, 3.0, 5.0, 7.0], + 1.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 2.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 4.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 8.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 0.17 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12], + 0.18 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12, 1.26, 1.68, 2.0, 3.0, 5.0, 7.0], + 0.25 : [1.0, 3.0, 5.0, 7.0], + 0.5 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 20.0 : [0.42] +} +################################################### +# GDS file info +################################################### +GDS = {} +# gds units +# From http://www.cnf.cornell.edu/cnf_spie9.html: "The first +# is the size of a database unit in user units. The second is the size +# of a database unit in meters. For example, if your library was +# created with the default units (user unit = 1 um and 1000 database +# units per user unit), then the first number would be 0.001 and the +# second number would be 10-9. Typically, the first number is less than +# 1, since you use more than 1 database unit per user unit. To +# calculate the size of a user unit in meters, divide the second number +# by the first." +GDS["unit"] = (0.001, 1e-9) +#GDS["unit"]=(0.001, 1e-6) + +################################################### +# Interconnect stacks +################################################### + +poly_stack = ("poly", "contact", "li") +active_stack = ("active", "contact", "li") +li_stack = ("li", "mcon", "m1") +m1_stack = ("m1", "via1", "m2") +m2_stack = ("m2", "via2", "m3") +m3_stack = ("m3", "via3", "m4") +m4_stack = ("m4", "via4", "m5") + +lef_rom_interconnect = ["m1", "m2", "m3", "m4"] + +layer_indices = {"poly": 0, + "active": 0, + "nwell": 0, + "li": 1, + "m1": 2, + "m2": 3, + "m3": 4, + "m4": 5, + "m5": 6} + +# The FEOL stacks get us up to m1 +feol_stacks = [poly_stack, + active_stack, + li_stack] + +# The BEOL stacks are m1 and up +beol_stacks = [m1_stack, + m2_stack, + m3_stack, + m4_stack] + +layer_stacks = feol_stacks + beol_stacks + +preferred_directions = {"poly": "V", + "active": "V", + "li": "V", + "m1": "H", + "m2": "V", + "m3": "H", + "m4": "V", + "m5": "H"} + +################################################### +# GDS Layer Map +################################################### + + +layer = {} +layer["active"] = (65, 20) # diff +layer["activep"] = (65, 20) # diff +layer["tap"] = (65, 44) # tap +layer["pwellp"] = (122,16) +layer["nwell"] = (64, 20) # nwell +layer["dnwell"] = (64,18) +layer["nimplant"]= (93, 44) # nsdm +layer["pimplant"]= (94, 20) # psdm +layer["vtl"] = (125, 44) # lvtn +layer["vth"] = (78, 44) # hvtp (pmos only) +layer["thkox"] = (8, 0) +layer["poly"] = (66, 20) +layer["contact"] = (66, 44) # licon1 +layer["npc"] = (95, 20) # npc (nitride cut) +layer["li"] = (67, 20) # active li1 +layer["mcon"] = (67, 44) # mcon +layer["m1"] = (68, 20) # met1 +layer["m1p"] = (68, 5) # met1 pin +layer["via1"] = (68, 44) # via1 +layer["m2"] = (69, 20) # met2 +layer["m2p"] = (69, 5) # met2 pin +layer["via2"] = (69, 44) # via2 +layer["m3"] = (70, 20) # met3 +layer["m3p"] = (70, 5) # met3 pin +layer["via3"] = (70, 44) # via3 +layer["m4"] = (71, 20) # met4 +layer["m4p"] = (71, 5) # met4 pin +layer["via4"] = (71, 44) # via4 +layer["m5"] = (72, 20) # met5 +layer["m5p"] = (72, 5) # met5 pin +layer["boundary"]= (235, 4) +# specific boundary type to define standard cell regions for DRC +layer["stdc"] = (81, 4) +layer["mem"] = (81, 2) +# Not an official sky130 layer, but useful for router debug infos +layer["text"]= (234, 5) +# Excpected value according to sky130A tech file +# If calibre is enabled, these will be swapped below +#pin_purpose = 5 +label_purpose = 5 +#label_purpose = 16 +#pin_purpose = 16 +#label_purpose = 5 + +# pin_read purposes +special_purposes = {layer["nwell"][0]: [layer["nwell"][1], 5, 59, 16]} +#layer_override = {"VNB\x00": ["pwell",122]} +layer_override = {"vnb": layer["pwellp"], "VNB": layer["pwellp"]} +layer_override_name = {"vnb": "pwellp", "VNB": "pwellp"} +layer_override_purpose = {122: (64, 59)} +# Layer names for external PDKs +layer_names = {} +layer_names["active"] = "diff" +layer_names["activep"] = "diff" +layer_names["tap"] = "tap" +layer_names["pwellp"] = "pwellp" +layer_names["nwell"] = "nwell" +layer_names["dnwell"] = "dnwell" +layer_names["nimplant"]= "nsdm" +layer_names["pimplant"]= "psdm" +layer_names["vtl"] = "lvtn" +layer_names["vth"] = "hvtp" +layer_names["thkox"] = "thkox" +layer_names["poly"] = "poly" +layer_names["contact"] = "licon1" +layer_names["li"] = "li1" +layer_names["mcon"] = "mcon" +layer_names["m1"] = "met1" +layer_names["m1p"] = "met1" +layer_names["via1"] = "via" +layer_names["m2"] = "met2" +layer_names["m2p"] = "met2" +layer_names["via2"] = "via2" +layer_names["m3"] = "met3" +layer_names["m3p"] = "met3" +layer_names["via3"] = "via3" +layer_names["m4"] = "met4" +layer_names["m4p"] = "met4" +layer_names["via4"] = "via4" +layer_names["m5p"] = "met5" +layer_names["boundary"]= "boundary" +layer_names["stdc"] = "areaid.standardc" +layer_names["mem"] = "areaid.core" +layer_names["text"] = "text" + + +################################################### +# DRC/LVS Rules Setup +################################################### + +# technology parameter +parameter={} +# difftap.2b +parameter["min_tx_size"] = 0.150 +parameter["beta"] = 3 + +parameter["6T_inv_nmos_size"] = 0.205 +parameter["6T_inv_pmos_size"] = 0.09 +parameter["6T_access_size"] = 0.135 + +drc = d.design_rules("sky130") + +# grid size +drc["grid"] = 0.005 + +#DRC/LVS test set_up +# Switching between calibre and magic can be useful for development, +# it eventually should be deleted. +NDA_PDK_ROOT = os.environ.get("NDA_PDK_ROOT", False) +use_calibre = bool(NDA_PDK_ROOT) +use_calibre = False +use_klayout = False +if use_calibre: + # Correct order according to s8 + pin_purpose = 16 + label_purpose = 5 + + drc["drc_rules"] = NDA_PDK_ROOT + "/DRC/Calibre/s8_drcRules" + drc["lvs_rules"] = NDA_PDK_ROOT + "/LVS/Calibre/lvs_s8_opts" + drc["xrc_rules"] = NDA_PDK_ROOT + "/PEX/xRC/extLvsRules_s8_5lm" + drc["layer_map"] = NDA_PDK_ROOT + "/VirtuosoOA/libs/technology_library/s8phirs_10r.layermap" + +# minwidth_tx with contact (no dog bone transistors) +# difftap.2b +drc["minwidth_tx"] = 0.360 +drc["minlength_channel"] = 0.150 + +drc["pwell_to_nwell"] = 0 +# nwell.1 Minimum width of nwell/pwell +drc.add_layer("nwell", + width=0.840, + spacing=1.270) + +# poly.1a Minimum width of poly +# poly.2 Minimum spacing of poly AND active +drc.add_layer("poly", + width=0.150, + spacing=0.210) +# poly.8 +drc["poly_extend_active"] = 0.13 +# Not a rule +drc["poly_to_contact"] = 0 +# poly.7 Minimum enclosure of active around gate +drc["active_enclose_gate"] = 0.075 +# poly.4 Minimum spacing of field poly to active +drc["poly_to_active"] = 0.075 +# poly.2 Minimum spacing of field poly +drc["poly_to_field_poly"] = 0.210 + +# difftap.1 Minimum width of active +# difftap.3 Minimum spacing of active +drc.add_layer("active", + width=0.150, + spacing=0.270) +# difftap.8 +drc.add_enclosure("nwell", + layer="active", + enclosure=0.18, + extension=0.18) + +# nsd/psd.5a +drc.add_enclosure("implant", + layer="active", + enclosure=0.125) + +# Same as active enclosure? +drc["implant_to_contact"] = 0.070 +# nsd/psd.1 nsd/psd.2 +drc.add_layer("implant", + width=0.380, + spacing=0.380, + area=0.265) + +# licon.1, licon.2 +drc.add_layer("contact", + width=0.170, + spacing=0.170) +# licon.5c (0.06 extension), (licon.7 for extension) +drc.add_enclosure("active", + layer="contact", + enclosure=0.040, + extension=0.060) +# licon.7 +drc["tap_extend_contact"] = 0.120 + +# licon.8 Minimum enclosure of poly around contact +drc.add_enclosure("poly", + layer="contact", + enclosure=0.08, + extension=0.08) +# licon.11a +drc["active_contact_to_gate"] = 0.050 +# npc.4 > licon.14 0.19 > licon.11a +drc["poly_contact_to_gate"] = 0.270 +# licon.15 +drc["npc_enclose_poly"] = 0.1 + +# li.1, li.3 +drc.add_layer("li", + width=0.170, + spacing=0.170) + +# licon.5 +drc.add_enclosure("li", + layer="contact", + enclosure=0, + extension=0.080) + +drc.add_enclosure("li", + layer="mcon", + enclosure=0, + extension=0.080) +# mcon.1, mcon.2 +drc.add_layer("mcon", + width=0.170, + spacing=0.210) + +# m1.1 Minimum width of metal1 +# m1.2 Minimum spacing of metal1 +# m1.6 Minimum area of metal1 +drc.add_layer("m1", + width=0.140, + spacing=0.140, + area=0.083) +# m1.4 Minimum enclosure of metal1 +# m1.5 Minimum enclosure around contact on two opposite sides +drc.add_enclosure("m1", + layer="mcon", + enclosure=0.030, + extension=0.060) +# via.4a Minimum enclosure around via1 +# via.5a Minimum enclosure around via1 on two opposite sides +drc.add_enclosure("m1", + layer="via1", + enclosure=0.055, + extension=0.085) + +# via.1a Minimum width of via1 +# via.2 Minimum spacing of via1 +drc.add_layer("via1", + width=0.150, + spacing=0.170) + +# m2.1 Minimum width of intermediate metal +# m2.2 Minimum spacing of intermediate metal +# m2.6 Minimum area of metal2 +drc.add_layer("m2", + width=0.140, + spacing=0.140, + area=0.0676) +# m2.4 Minimum enclosure around via1 +# m2.5 Minimum enclosure around via1 on two opposite sides +drc.add_enclosure("m2", + layer="via1", + enclosure=0.055, + extension=0.085) +# via2.4 Minimum enclosure around via2 +# via2.5 Minimum enclosure around via2 on two opposite sides +drc.add_enclosure("m2", + layer="via2", + enclosure=0.040, + extension=0.085) + +# via2.1a Minimum width of Via2 +# via2.2 Minimum spacing of Via2 +drc.add_layer("via2", + width=0.200, + spacing=0.200) + +# m3.1 Minimum width of metal3 +# m3.2 Minimum spacing of metal3 +# m3.6 Minimum area of metal3 +drc.add_layer("m3", + width=0.300, + spacing=0.300, + area=0.240) +# m3.4 Minimum enclosure around via2 +drc.add_enclosure("m3", + layer="via2", + enclosure=0.065) +# via3.4 Minimum enclosure around via3 +# via3.5 Minimum enclosure around via3 on two opposite sides +drc.add_enclosure("m3", + layer="via3", + enclosure=0.060, + extension=0.090) + +# via3.1 Minimum width of Via3 +# via3.2 Minimum spacing of Via3 +drc.add_layer("via3", + width=0.200, + spacing=0.200) + +# m4.1 Minimum width of metal4 +# m4.2 Minimum spacing of metal4 +# m4.7 Minimum area of metal4 +drc.add_layer("m4", + width=0.300, + spacing=0.300, + area=0.240) +# m4.3 Minimum enclosure around via3 +drc.add_enclosure("m4", + layer="via3", + enclosure=0.065) +# FIXME: Wrong rule m4.3 Minimum enclosure around via3 +drc.add_enclosure("m4", + layer="via4", + enclosure=0.060) + + +# via4.1 Minimum width of Via4 +# via4.2 Minimum spacing of Via4 +drc.add_layer("via4", + width=0.800, + spacing=0.800) + +# FIXME: Wrong rules +# m5.1 Minimum width of metal5 +# m5.2 Minimum spacing of metal5 +# m5.7 Minimum area of metal5 +drc.add_layer("m5", + width=1.600, + spacing=1.600, + area=4.000) +# m5.3 Minimum enclosure around via4 +drc.add_enclosure("m5", + layer="via4", + enclosure=0.310) + + + +# Metal 5-10 are ommitted + +################################################### +# Spice Simulation Parameters +################################################### + +# spice info +spice = {} +spice["nmos"] = "sky130_fd_pr__nfet_01v8" +spice["pmos"] = "sky130_fd_pr__pfet_01v8" +spice["power"]="vccd1" +spice["ground"]="vssd1" + +# whether or not the device model is actually a subckt +spice["device_prefix"] = "X" + +spice["fet_libraries"] = { "TT": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "tt"]], + "SS": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "ss"]], + "FF": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "ff"]], + "SF": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "sf"]], + "FS": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "fs"]] } + +# spice stimulus related variables +spice["feasible_period"] = 10 # estimated feasible period in ns +spice["supply_voltages"] = [1.7, 1.8, 1.9] # Supply voltage corners in [Volts] +spice["nom_supply_voltage"] = 1.8 # Nominal supply voltage in [Volts] +spice["rise_time"] = 0.005 # rise time in [Nano-seconds] +spice["fall_time"] = 0.005 # fall time in [Nano-seconds] +spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius) +spice["nom_temperature"] = 25 # Nominal temperature (celcius) + +# analytical delay parameters +spice["nom_threshold"] = 0.49 # Typical Threshold voltage in Volts +spice["wire_unit_r"] = 0.125 # Unit wire resistance in ohms/square +spice["wire_unit_c"] = 0.134 # Unit wire capacitance ff/um^2 +spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff +spice["min_tx_gate_c"] = 0.2 # Minimum transistor gate capacitance in ff +spice["dff_setup"] = 102.5391 # DFF setup time in ps +spice["dff_hold"] = -56 # DFF hold time in ps +spice["dff_in_cap"] = 6.89 # Input capacitance (D) [Femto-farad] +spice["dff_out_cap"] = 6.89 # Output capacitance (Q) [Femto-farad] + +# analytical power parameters, many values are temporary +spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW +spice["inv_leakage"] = 1 # Leakage power of inverter in nW +spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW +spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW +spice["nand4_leakage"] = 1 # Leakage power of 4-input nand in nW +spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW +spice["dff_leakage"] = 1 # Leakage power of flop in nW + +spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz + +# Parameters related to sense amp enable timing and delay chain/RBL sizing +parameter["le_tau"] = 2.25 # In pico-seconds. +parameter["cap_relative_per_ff"] = 7.5 # Units of Relative Capacitance/ Femto-Farad +parameter["dff_clk_cin"] = 30.6 # relative capacitance +parameter["6tcell_wl_cin"] = 3 # relative capacitance +parameter["min_inv_para_delay"] = 2.4 # Tau delay units +parameter["sa_en_pmos_size"] = 0.72 # micro-meters +parameter["sa_en_nmos_size"] = 0.27 # micro-meters +parameter["sa_inv_pmos_size"] = 0.54 # micro-meters +parameter["sa_inv_nmos_size"] = 0.27 # micro-meters +parameter["bitcell_drain_cap"] = 0.1 # In Femto-Farad, approximation of drain capacitance + +################################################### +# Technology Tool Preferences +################################################### + +if use_calibre: + drc_name = "calibre" + lvs_name = "calibre" + pex_name = "calibre" +elif use_klayout: + drc_name = "klayout" + lvs_name = "klayout" + pex_name = "klayout" +else: + drc_name = "magic" + lvs_name = "netgen" + pex_name = "magic" + + +# This is used by uniqify to not rename the library cells +library_prefix_name = "sky130_fd_bd_sram__" +# List of cells to skip running DRC/LVS on directly +# This will look for a maglef file and copy it over the mag file +# before DRC after extraction + +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_cell +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_ce +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_ce +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_cell +# gds flatglob sky130_fd_bd_sram__sram_sp_cell_fom_serifs + +flatglob = ["*_?mos_m*", + "sky130_fd_bd_sram__sram_sp_cell_fom_serifs", + + "sky130_fd_bd_sram__sram_sp_cell", + "sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_cell", + + "sky130_fd_bd_sram__sram_sp_cell_opt1_ce", + "sky130_fd_bd_sram__openram_sp_cell_opt1_replica_ce", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap_p_ce"] + +blackbox_cells = ["sky130_fd_bd_sram__openram_dp_cell", + "sky130_fd_bd_sram__openram_dp_cell_dummy", + "sky130_fd_bd_sram__openram_dp_cell_replica", + + "sky130_fd_bd_sram__openram_sp_cell_opt1_noblcon", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_noblcon", + "sky130_fd_bd_sram__openram_sp_colend_replica", + "sky130_fd_bd_sram__openram_sp_colenda_replica", + "sky130_fd_bd_sram__sram_sp_cell_opt1a", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy", + "sky130_fd_bd_sram__openram_sp_cell_opt1_dummy", + "sky130_fd_bd_sram__sram_sp_cell_opt1_ce", + "sky130_fd_bd_sram__sram_sp_cell_opt1", + "sky130_fd_bd_sram__openram_sp_cell_opt1_replica", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica", + "sky130_fd_bd_sram__sram_sp_colend", + "sky130_fd_bd_sram__sram_sp_colend_cent", + "sky130_fd_bd_sram__sram_sp_colend_p_cent", + "sky130_fd_bd_sram__sram_sp_colenda", + "sky130_fd_bd_sram__sram_sp_colenda_cent", + "sky130_fd_bd_sram__sram_sp_colenda_p_cent", + "sky130_fd_bd_sram__sram_sp_rowend", + "sky130_fd_bd_sram__sram_sp_rowenda", + "sky130_fd_bd_sram__openram_sp_rowend_replica", + "sky130_fd_bd_sram__openram_sp_rowenda_replica", + "sky130_fd_bd_sram__sram_sp_corner", + "sky130_fd_bd_sram__sram_sp_cornera", + "sky130_fd_bd_sram__sram_sp_cornerb", + "sky130_fd_bd_sram__sram_sp_wlstrapa", + "sky130_fd_bd_sram__sram_sp_wlstrap_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap", + "sky130_fd_bd_sram__sram_sp_wlstrap_p_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap_p"] \ No newline at end of file diff --git a/technology/sky130/tech/tech_configs/tech_cypress_cell.py b/technology/sky130/tech/tech_configs/tech_cypress_cell.py new file mode 100644 index 00000000..bfc44ff5 --- /dev/null +++ b/technology/sky130/tech/tech_configs/tech_cypress_cell.py @@ -0,0 +1,848 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California +# All rights reserved. +# + + +import os +from openram import drc as d + +""" +File containing the process technology parameters for Skywater 130nm. +""" + +################################################### +# Custom modules +################################################### + +# This uses the default classes to instantiate module from +# '$OPENRAM_HOME/compiler/modules'. +# Using tech_modules['cellname'] you can override each class by providing a custom +# implementation in '$OPENRAM_TECHDIR/modules/' +# For example: tech_modules["contact"] = "contact_freepdk45" +tech_modules = d.module_type() + +# These modules have been hand designed and provided in this repository. +tech_modules["nand2_dec"] = "nand2_dec" +tech_modules["nand3_dec"] = "nand3_dec" +tech_modules["nand4_dec"] = "nand4_dec" + +# Override default OpenRAM modules to sky130 modules +# These are for single port and dual port as a list, +# or for both if there is no list, +# or only applicable to one if there is no list. +tech_modules["bitcell_1port"] = "sky130_bitcell" +tech_modules["replica_bitcell_1port"] = "sky130_replica_bitcell" +tech_modules["dummy_bitcell_1port"] = "sky130_dummy_bitcell" + +tech_modules["replica_bitcell_2port"] = "replica_bitcell_2port" +tech_modules["dummy_bitcell_2port"] = "dummy_bitcell_2port" +tech_modules["bitcell_2port"] = "bitcell_2port" + +tech_modules["bitcell_array"] = ["sky130_bitcell_array", "bitcell_array"] +tech_modules["replica_bitcell_array"] = ["sky130_replica_bitcell_array", "replica_bitcell_array"] +tech_modules["capped_replica_bitcell_array"] = ["sky130_capped_replica_bitcell_array", "capped_replica_bitcell_array"] +tech_modules["dummy_array"] = ["sky130_dummy_array", "dummy_array"] + +tech_modules["replica_column"] = ["sky130_replica_column", "replica_column"] + +tech_modules["col_cap_array"] = ["sky130_col_cap_array", "col_cap_array"] +tech_modules["col_cap"] = ["sky130_col_cap", "col_cap_bitcell_2port"] +tech_modules["corner"] = ["sky130_corner", None] +tech_modules["internal"] = ["sky130_internal", None] +tech_modules["row_cap_array"] = ["sky130_row_cap_array", "row_cap_array"] +tech_modules["row_cap"] = ["sky130_row_cap", "row_cap_bitcell_2port"] + +# These modules are auto-generated from the nand decoders above and are not +# found in this. +tech_modules["buf_dec"] = "pbuf_dec" +tech_modules["inv_dec"] = "pinv_dec" +tech_modules["and2_dec"] = "and2_dec" +tech_modules["and3_dec"] = "and3_dec" +tech_modules["and4_dec"] = "and4_dec" + +################################################### +# Custom cell properties +################################################### +cell_properties = d.cell_properties() + +cell_properties.bitcell_power_pin_directions = ("H", "H") + +cell_properties.bitcell_1port.mirror.x = True +cell_properties.bitcell_1port.mirror.y = True +cell_properties.bitcell_1port.end_caps = True +cell_properties.bitcell_1port.has_corners = True +cell_properties.bitcell_1port.boundary_layer = "mem" +cell_properties.bitcell_1port.port_order = ['bl', 'br', 'gnd', 'vdd', 'vpb', 'vnb', 'wl'] +cell_properties.bitcell_1port.port_types = ["OUTPUT", "OUTPUT", "GROUND", "POWER", "BIAS", "BIAS", "INPUT"] +cell_properties.bitcell_1port.port_map = {'bl': 'BL', + 'br': 'BR', + 'wl': 'WL', + 'vdd': 'VPWR', + 'vnb': 'VNB', + 'vpb': 'VPB', + 'gnd': 'VGND'} + +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.bl_layer = "m1" +cell_properties.bitcell_1port.vdd_layer = "m1" +cell_properties.bitcell_1port.vdd_dir = "V" +cell_properties.bitcell_1port.gnd_layer = "m2" +cell_properties.bitcell_1port.gnd_dir = "H" + +cell_properties.bitcell_2port.mirror.x = True +cell_properties.bitcell_2port.mirror.y = True +cell_properties.bitcell_2port.end_caps = True +cell_properties.bitcell_2port.has_corners = True +cell_properties.bitcell_2port.port_order = ['bl0', 'br0', 'bl1', 'br1', 'wl0', 'wl1', 'vdd', 'gnd'] +cell_properties.bitcell_2port.port_map = {'bl0': 'BL0', + 'br0': 'BR0', + 'bl1': 'BL1', + 'br1': 'BR1', + 'wl0': 'WL0', + 'wl1': 'WL1', + 'vdd': 'VDD', + 'gnd': 'GND'} +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.vdd_layer = "m2" +cell_properties.bitcell_1port.vdd_dir = "H" +cell_properties.bitcell_1port.gnd_layer = "m2" +cell_properties.bitcell_1port.gnd_dir = "H" +cell_properties.bitcell_2port.wl_layer = "m2" +cell_properties.bitcell_2port.vdd_layer = "m1" +cell_properties.bitcell_2port.vdd_dir = "H" +cell_properties.bitcell_2port.gnd_layer = "m2" +cell_properties.bitcell_2port.gnd_dir = "H" + +cell_properties.col_cap_1port_bitcell = d.cell(['bl', 'br', 'vdd', 'gnd', 'vpb', 'vnb', 'gate'], + ['INPUT', 'INPUT','POWER', 'GROUND', 'BIAS', 'BIAS', 'INPUT'], + {'bl': 'bl', + 'br': 'br', + 'vdd': 'vdd', + 'gnd': 'gnd', + 'vnb': 'vnb', + 'vpb': 'vpb', + 'gate': 'gate'}) +cell_properties.col_cap_1port_bitcell.boundary_layer = "mem" + +cell_properties.col_cap_1port_strap_power = d.cell(['vdd', 'vpb', 'vnb'], + ['POWER', 'BIAS', 'BIAS'], + {'vnb': 'VNB', + 'vpb': 'VPB', + 'vdd': 'VPWR'}) +cell_properties.col_cap_1port_strap_power.boundary_layer = "mem" + +cell_properties.col_cap_1port_strap_ground = d.cell(['gnd', 'vpb', 'vnb'], + ['GROUND', 'BIAS', 'BIAS'], + {'vnb': 'VNB', + 'vpb': 'VPB', + 'gnd': 'VGND'}) +cell_properties.col_cap_1port_strap_ground.boundary_layer = "mem" + +cell_properties.row_cap_1port_cell = d.cell(['vdd', 'wl'], + ['POWER', 'INPUT'], + {'wl': 'WL', + 'vdd': 'VPWR'}) +cell_properties.row_cap_1port_cell.boundary_layer = "mem" + +cell_properties.col_cap_2port.port_order = ['bl0', 'br0', 'bl1', 'br1', 'vdd'] +cell_properties.col_cap_2port.port_map = {'bl0': 'BL0', + 'br0': 'BR0', + 'bl1': 'BL1', + 'br1': 'BR1', + 'vdd': 'VDD'} + +cell_properties.row_cap_2port.port_order = ['wl0', 'wl1', 'gnd'] +cell_properties.row_cap_2port.port_map = {'wl0': 'WL0', + 'wl1': 'WL1', + 'gnd': 'GND'} + + +cell_properties.ptx.bin_spice_models = True +cell_properties.ptx.model_is_subckt = True + +cell_properties.pgate.add_implants = True + +cell_properties.use_strap = True +cell_properties.strap_module = "internal" +cell_properties.strap_version = "wlstrap" + +cell_properties.dff.port_order = ['D', 'Q', 'clk', 'vdd', 'gnd'] +cell_properties.dff.port_map = {'D': 'D', + 'Q': 'Q', + 'clk': 'CLK', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.nand2_dec.port_order = ['A', 'B', 'Z', 'vdd', 'gnd'] +cell_properties.nand2_dec.port_map = {'A': 'A', + 'B': 'B', + 'Z': 'Z', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.nand3_dec.port_order = ['A', 'B', 'C', 'Z', 'vdd', 'gnd'] +cell_properties.nand3_dec.port_map = {'A': 'A', + 'B': 'B', + 'C': 'C', + 'Z': 'Z', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.nand4_dec.port_order = ['A', 'B', 'C', 'D', 'Z', 'vdd', 'gnd'] +cell_properties.nand4_dec.port_map = {'A': 'A', + 'B': 'B', + 'C': 'C', + 'D': 'D', + 'Z': 'Z', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.sense_amp.port_order = ['bl', 'br', 'dout', 'en', 'vdd', 'gnd'] +cell_properties.sense_amp.port_map = {'bl': 'BL', + 'br': 'BR', + 'dout': 'DOUT', + 'en': 'EN', + 'vdd': 'VDD', + 'gnd': 'GND'} + +cell_properties.write_driver.port_order = ['din', 'bl', 'br', 'en', 'vdd', 'gnd'] +cell_properties.write_driver.port_map = {'din': 'DIN', + 'bl': 'BL', + 'br': 'BR', + 'en': 'EN', + 'vdd': 'VDD', + 'gnd': 'GND'} + + +# You can override the GDS for custom cell using the following: +# If it is a list, the first is single port and the second is dual port. +# If it is string, it is used for both single and dual port. +cell_properties.names["dff"] = "sky130_fd_bd_sram__openram_dff" +cell_properties.names["nand2_dec"] = ["sky130_fd_bd_sram__openram_sp_nand2_dec", "sky130_fd_bd_sram__openram_dp_nand2_dec"] +cell_properties.names["nand3_dec"] = ["sky130_fd_bd_sram__openram_sp_nand3_dec", "sky130_fd_bd_sram__openram_dp_nand3_dec"] +cell_properties.names["nand4_dec"] = ["sky130_fd_bd_sram__openram_sp_nand4_dec", "sky130_fd_bd_sram__openram_dp_nand4_dec"] + +cell_properties.names["bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell" +cell_properties.names["dummy_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_dummy" +cell_properties.names["replica_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_replica" +cell_properties.names["col_cap_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_cap_col" +cell_properties.names["row_cap_bitcell_2port"] = "sky130_fd_bd_sram__openram_dp_cell_cap_row" +cell_properties.names["sense_amp"] = "sky130_fd_bd_sram__openram_sense_amp" +cell_properties.names["write_driver"] = "sky130_fd_bd_sram__openram_write_driver" + +array_row_multiple = 2 +array_col_multiple = 2 + +################################################### +# Custom layer properties +################################################### +layer_properties = d.layer_properties() +layer_properties.hierarchical_decoder.bus_layer = "m1" +layer_properties.hierarchical_decoder.bus_directions = "nonpref" +layer_properties.hierarchical_decoder.input_layer = "li" +layer_properties.hierarchical_decoder.output_layer = "m2" +layer_properties.hierarchical_decoder.vertical_supply = True + +layer_properties.hierarchical_predecode.bus_layer = "m1" +layer_properties.hierarchical_predecode.bus_directions = "nonpref" +# This is added to allow the column decoder connections on m2 +layer_properties.hierarchical_predecode.bus_pitch_factor = 1.2 +layer_properties.hierarchical_predecode.bus_space_factor = 1.5 +layer_properties.hierarchical_predecode.input_layer = "li" +layer_properties.hierarchical_predecode.output_layer = "m2" +layer_properties.hierarchical_predecode.vertical_supply = True +layer_properties.hierarchical_predecode.force_horizontal_input_contact = True + +layer_properties.bank.stack = "m2_stack" +layer_properties.bank.pitch = "m3_pitch" + +layer_properties.column_mux_array.select_layer = "m3" +layer_properties.column_mux_array.bitline_layer = "m1" + +layer_properties.port_address.supply_offset = True + +layer_properties.port_data.enable_layer = "m1" +layer_properties.port_data.channel_route_bitlines = False + +layer_properties.replica_column.even_rows = True + +layer_properties.wordline_driver.vertical_supply = True + +layer_properties.global_wordline_layer = "m5" + + +################################################### +# Discrete tx bins +################################################### +# enforce that tx sizes are within 25% of requested size after fingering. +accuracy_requirement = 0.75 +nmos_bins = { + 0.15 : [0.36, 0.39, 0.42, 0.52, 0.54, 0.55, 0.58, 0.6, 0.61, 0.64, 0.65, 0.74, 0.84, 1.0, 1.26, 1.68, 2.0, 3.0, 5.0, 7.0], + 0.18 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 0.25 : [0.65, 1.0, 3.0, 5.0, 7.0], + 0.5 : [0.42, 0.55, 0.65, 1.0, 3.0, 5.0, 7.0], + 1.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 2.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 4.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 8.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0], + 20.0 : [0.42, 0.65, 1.0, 3.0, 5.0, 7.0] +} + +pmos_bins = { + 0.15 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12, 1.26, 1.65, 1.68, 2.0, 3.0, 5.0, 7.0], + 1.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 2.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 4.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 8.0 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 0.17 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12], + 0.18 : [0.42, 0.55, 0.64, 0.84, 1.0, 1.12, 1.26, 1.68, 2.0, 3.0, 5.0, 7.0], + 0.25 : [1.0, 3.0, 5.0, 7.0], + 0.5 : [0.42, 0.55, 1.0, 3.0, 5.0, 7.0], + 20.0 : [0.42] +} +################################################### +# GDS file info +################################################### +GDS = {} +# gds units +# From http://www.cnf.cornell.edu/cnf_spie9.html: "The first +# is the size of a database unit in user units. The second is the size +# of a database unit in meters. For example, if your library was +# created with the default units (user unit = 1 um and 1000 database +# units per user unit), then the first number would be 0.001 and the +# second number would be 10-9. Typically, the first number is less than +# 1, since you use more than 1 database unit per user unit. To +# calculate the size of a user unit in meters, divide the second number +# by the first." +GDS["unit"] = (0.001, 1e-9) +#GDS["unit"]=(0.001, 1e-6) + +################################################### +# Interconnect stacks +################################################### + +poly_stack = ("poly", "contact", "li") +active_stack = ("active", "contact", "li") +li_stack = ("li", "mcon", "m1") +m1_stack = ("m1", "via1", "m2") +m2_stack = ("m2", "via2", "m3") +m3_stack = ("m3", "via3", "m4") +m4_stack = ("m4", "via4", "m5") + +lef_rom_interconnect = ["m1", "m2", "m3", "m4"] + +layer_indices = {"poly": 0, + "active": 0, + "nwell": 0, + "li": 1, + "m1": 2, + "m2": 3, + "m3": 4, + "m4": 5, + "m5": 6} + +# The FEOL stacks get us up to m1 +feol_stacks = [poly_stack, + active_stack, + li_stack] + +# The BEOL stacks are m1 and up +beol_stacks = [m1_stack, + m2_stack, + m3_stack, + m4_stack] + +layer_stacks = feol_stacks + beol_stacks + +preferred_directions = {"poly": "V", + "active": "V", + "li": "V", + "m1": "H", + "m2": "V", + "m3": "H", + "m4": "V", + "m5": "H"} + +################################################### +# GDS Layer Map +################################################### + + +layer = {} +layer["active"] = (65, 20) # diff +layer["activep"] = (65, 20) # diff +layer["tap"] = (65, 44) # tap +layer["pwellp"] = (122,16) +layer["nwell"] = (64, 20) # nwell +layer["dnwell"] = (64,18) +layer["nimplant"]= (93, 44) # nsdm +layer["pimplant"]= (94, 20) # psdm +layer["vtl"] = (125, 44) # lvtn +layer["vth"] = (78, 44) # hvtp (pmos only) +layer["thkox"] = (8, 0) +layer["poly"] = (66, 20) +layer["contact"] = (66, 44) # licon1 +layer["npc"] = (95, 20) # npc (nitride cut) +layer["li"] = (67, 20) # active li1 +layer["mcon"] = (67, 44) # mcon +layer["m1"] = (68, 20) # met1 +layer["m1p"] = (68, 5) # met1 pin +layer["via1"] = (68, 44) # via1 +layer["m2"] = (69, 20) # met2 +layer["m2p"] = (69, 5) # met2 pin +layer["via2"] = (69, 44) # via2 +layer["m3"] = (70, 20) # met3 +layer["m3p"] = (70, 5) # met3 pin +layer["via3"] = (70, 44) # via3 +layer["m4"] = (71, 20) # met4 +layer["m4p"] = (71, 5) # met4 pin +layer["via4"] = (71, 44) # via4 +layer["m5"] = (72, 20) # met5 +layer["m5p"] = (72, 5) # met5 pin +layer["boundary"]= (235, 4) +# specific boundary type to define standard cell regions for DRC +layer["stdc"] = (81, 4) +layer["mem"] = (81, 2) +# Not an official sky130 layer, but useful for router debug infos +layer["text"]= (234, 5) +# Excpected value according to sky130A tech file +# If calibre is enabled, these will be swapped below +#pin_purpose = 5 +label_purpose = 5 +#label_purpose = 16 +#pin_purpose = 16 +#label_purpose = 5 + +# pin_read purposes +special_purposes = {layer["nwell"][0]: [layer["nwell"][1], 5, 59, 16]} +#layer_override = {"VNB\x00": ["pwell",122]} +layer_override = {"vnb": layer["pwellp"], "VNB": layer["pwellp"]} +layer_override_name = {"vnb": "pwellp", "VNB": "pwellp"} +layer_override_purpose = {122: (64, 59)} +# Layer names for external PDKs +layer_names = {} +layer_names["active"] = "diff" +layer_names["activep"] = "diff" +layer_names["tap"] = "tap" +layer_names["pwellp"] = "pwellp" +layer_names["nwell"] = "nwell" +layer_names["dnwell"] = "dnwell" +layer_names["nimplant"]= "nsdm" +layer_names["pimplant"]= "psdm" +layer_names["vtl"] = "lvtn" +layer_names["vth"] = "hvtp" +layer_names["thkox"] = "thkox" +layer_names["poly"] = "poly" +layer_names["contact"] = "licon1" +layer_names["li"] = "li1" +layer_names["mcon"] = "mcon" +layer_names["m1"] = "met1" +layer_names["m1p"] = "met1" +layer_names["via1"] = "via" +layer_names["m2"] = "met2" +layer_names["m2p"] = "met2" +layer_names["via2"] = "via2" +layer_names["m3"] = "met3" +layer_names["m3p"] = "met3" +layer_names["via3"] = "via3" +layer_names["m4"] = "met4" +layer_names["m4p"] = "met4" +layer_names["via4"] = "via4" +layer_names["m5p"] = "met5" +layer_names["boundary"]= "boundary" +layer_names["stdc"] = "areaid.standardc" +layer_names["mem"] = "areaid.core" +layer_names["text"] = "text" + + +################################################### +# DRC/LVS Rules Setup +################################################### + +# technology parameter +parameter={} +# difftap.2b +parameter["min_tx_size"] = 0.150 +parameter["beta"] = 3 + +parameter["6T_inv_nmos_size"] = 0.205 +parameter["6T_inv_pmos_size"] = 0.09 +parameter["6T_access_size"] = 0.135 + +drc = d.design_rules("sky130") + +# grid size +drc["grid"] = 0.005 + +#DRC/LVS test set_up +# Switching between calibre and magic can be useful for development, +# it eventually should be deleted. +NDA_PDK_ROOT = os.environ.get("NDA_PDK_ROOT", False) +use_calibre = bool(NDA_PDK_ROOT) +use_calibre = False +use_klayout = False +if use_calibre: + # Correct order according to s8 + pin_purpose = 16 + label_purpose = 5 + + drc["drc_rules"] = NDA_PDK_ROOT + "/DRC/Calibre/s8_drcRules" + drc["lvs_rules"] = NDA_PDK_ROOT + "/LVS/Calibre/lvs_s8_opts" + drc["xrc_rules"] = NDA_PDK_ROOT + "/PEX/xRC/extLvsRules_s8_5lm" + drc["layer_map"] = NDA_PDK_ROOT + "/VirtuosoOA/libs/technology_library/s8phirs_10r.layermap" + +# minwidth_tx with contact (no dog bone transistors) +# difftap.2b +drc["minwidth_tx"] = 0.360 +drc["minlength_channel"] = 0.150 + +drc["pwell_to_nwell"] = 0 +# nwell.1 Minimum width of nwell/pwell +drc.add_layer("nwell", + width=0.840, + spacing=1.270) + +# poly.1a Minimum width of poly +# poly.2 Minimum spacing of poly AND active +drc.add_layer("poly", + width=0.150, + spacing=0.210) +# poly.8 +drc["poly_extend_active"] = 0.13 +# Not a rule +drc["poly_to_contact"] = 0 +# poly.7 Minimum enclosure of active around gate +drc["active_enclose_gate"] = 0.075 +# poly.4 Minimum spacing of field poly to active +drc["poly_to_active"] = 0.075 +# poly.2 Minimum spacing of field poly +drc["poly_to_field_poly"] = 0.210 + +# difftap.1 Minimum width of active +# difftap.3 Minimum spacing of active +drc.add_layer("active", + width=0.150, + spacing=0.270) +# difftap.8 +drc.add_enclosure("nwell", + layer="active", + enclosure=0.18, + extension=0.18) + +# nsd/psd.5a +drc.add_enclosure("implant", + layer="active", + enclosure=0.125) + +# Same as active enclosure? +drc["implant_to_contact"] = 0.070 +# nsd/psd.1 nsd/psd.2 +drc.add_layer("implant", + width=0.380, + spacing=0.380, + area=0.265) + +# licon.1, licon.2 +drc.add_layer("contact", + width=0.170, + spacing=0.170) +# licon.5c (0.06 extension), (licon.7 for extension) +drc.add_enclosure("active", + layer="contact", + enclosure=0.040, + extension=0.060) +# licon.7 +drc["tap_extend_contact"] = 0.120 + +# licon.8 Minimum enclosure of poly around contact +drc.add_enclosure("poly", + layer="contact", + enclosure=0.08, + extension=0.08) +# licon.11a +drc["active_contact_to_gate"] = 0.050 +# npc.4 > licon.14 0.19 > licon.11a +drc["poly_contact_to_gate"] = 0.270 +# licon.15 +drc["npc_enclose_poly"] = 0.1 + +# li.1, li.3 +drc.add_layer("li", + width=0.170, + spacing=0.170) + +# licon.5 +drc.add_enclosure("li", + layer="contact", + enclosure=0, + extension=0.080) + +drc.add_enclosure("li", + layer="mcon", + enclosure=0, + extension=0.080) +# mcon.1, mcon.2 +drc.add_layer("mcon", + width=0.170, + spacing=0.210) + +# m1.1 Minimum width of metal1 +# m1.2 Minimum spacing of metal1 +# m1.6 Minimum area of metal1 +drc.add_layer("m1", + width=0.140, + spacing=0.140, + area=0.083) +# m1.4 Minimum enclosure of metal1 +# m1.5 Minimum enclosure around contact on two opposite sides +drc.add_enclosure("m1", + layer="mcon", + enclosure=0.030, + extension=0.060) +# via.4a Minimum enclosure around via1 +# via.5a Minimum enclosure around via1 on two opposite sides +drc.add_enclosure("m1", + layer="via1", + enclosure=0.055, + extension=0.085) + +# via.1a Minimum width of via1 +# via.2 Minimum spacing of via1 +drc.add_layer("via1", + width=0.150, + spacing=0.170) + +# m2.1 Minimum width of intermediate metal +# m2.2 Minimum spacing of intermediate metal +# m2.6 Minimum area of metal2 +drc.add_layer("m2", + width=0.140, + spacing=0.140, + area=0.0676) +# m2.4 Minimum enclosure around via1 +# m2.5 Minimum enclosure around via1 on two opposite sides +drc.add_enclosure("m2", + layer="via1", + enclosure=0.055, + extension=0.085) +# via2.4 Minimum enclosure around via2 +# via2.5 Minimum enclosure around via2 on two opposite sides +drc.add_enclosure("m2", + layer="via2", + enclosure=0.040, + extension=0.085) + +# via2.1a Minimum width of Via2 +# via2.2 Minimum spacing of Via2 +drc.add_layer("via2", + width=0.200, + spacing=0.200) + +# m3.1 Minimum width of metal3 +# m3.2 Minimum spacing of metal3 +# m3.6 Minimum area of metal3 +drc.add_layer("m3", + width=0.300, + spacing=0.300, + area=0.240) +# m3.4 Minimum enclosure around via2 +drc.add_enclosure("m3", + layer="via2", + enclosure=0.065) +# via3.4 Minimum enclosure around via3 +# via3.5 Minimum enclosure around via3 on two opposite sides +drc.add_enclosure("m3", + layer="via3", + enclosure=0.060, + extension=0.090) + +# via3.1 Minimum width of Via3 +# via3.2 Minimum spacing of Via3 +drc.add_layer("via3", + width=0.200, + spacing=0.200) + +# m4.1 Minimum width of metal4 +# m4.2 Minimum spacing of metal4 +# m4.7 Minimum area of metal4 +drc.add_layer("m4", + width=0.300, + spacing=0.300, + area=0.240) +# m4.3 Minimum enclosure around via3 +drc.add_enclosure("m4", + layer="via3", + enclosure=0.065) +# FIXME: Wrong rule m4.3 Minimum enclosure around via3 +drc.add_enclosure("m4", + layer="via4", + enclosure=0.060) + + +# via4.1 Minimum width of Via4 +# via4.2 Minimum spacing of Via4 +drc.add_layer("via4", + width=0.800, + spacing=0.800) + +# FIXME: Wrong rules +# m5.1 Minimum width of metal5 +# m5.2 Minimum spacing of metal5 +# m5.7 Minimum area of metal5 +drc.add_layer("m5", + width=1.600, + spacing=1.600, + area=4.000) +# m5.3 Minimum enclosure around via4 +drc.add_enclosure("m5", + layer="via4", + enclosure=0.310) + + + +# Metal 5-10 are ommitted + +################################################### +# Spice Simulation Parameters +################################################### + +# spice info +spice = {} +spice["nmos"] = "sky130_fd_pr__nfet_01v8" +spice["pmos"] = "sky130_fd_pr__pfet_01v8" +spice["power"]="vccd1" +spice["ground"]="vssd1" + +# whether or not the device model is actually a subckt +spice["device_prefix"] = "X" + +spice["fet_libraries"] = { "TT": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "tt"]], + "SS": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "ss"]], + "FF": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "ff"]], + "SF": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "sf"]], + "FS": [[os.environ.get("SPICE_MODEL_DIR") + "/sky130.lib.spice", "fs"]] } + +# spice stimulus related variables +spice["feasible_period"] = 10 # estimated feasible period in ns +spice["supply_voltages"] = [1.7, 1.8, 1.9] # Supply voltage corners in [Volts] +spice["nom_supply_voltage"] = 1.8 # Nominal supply voltage in [Volts] +spice["rise_time"] = 0.005 # rise time in [Nano-seconds] +spice["fall_time"] = 0.005 # fall time in [Nano-seconds] +spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius) +spice["nom_temperature"] = 25 # Nominal temperature (celcius) + +# analytical delay parameters +spice["nom_threshold"] = 0.49 # Typical Threshold voltage in Volts +spice["wire_unit_r"] = 0.125 # Unit wire resistance in ohms/square +spice["wire_unit_c"] = 0.134 # Unit wire capacitance ff/um^2 +spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff +spice["min_tx_gate_c"] = 0.2 # Minimum transistor gate capacitance in ff +spice["dff_setup"] = 102.5391 # DFF setup time in ps +spice["dff_hold"] = -56 # DFF hold time in ps +spice["dff_in_cap"] = 6.89 # Input capacitance (D) [Femto-farad] +spice["dff_out_cap"] = 6.89 # Output capacitance (Q) [Femto-farad] + +# analytical power parameters, many values are temporary +spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW +spice["inv_leakage"] = 1 # Leakage power of inverter in nW +spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW +spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW +spice["nand4_leakage"] = 1 # Leakage power of 4-input nand in nW +spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW +spice["dff_leakage"] = 1 # Leakage power of flop in nW + +spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz + +# Parameters related to sense amp enable timing and delay chain/RBL sizing +parameter["le_tau"] = 2.25 # In pico-seconds. +parameter["cap_relative_per_ff"] = 7.5 # Units of Relative Capacitance/ Femto-Farad +parameter["dff_clk_cin"] = 30.6 # relative capacitance +parameter["6tcell_wl_cin"] = 3 # relative capacitance +parameter["min_inv_para_delay"] = 2.4 # Tau delay units +parameter["sa_en_pmos_size"] = 0.72 # micro-meters +parameter["sa_en_nmos_size"] = 0.27 # micro-meters +parameter["sa_inv_pmos_size"] = 0.54 # micro-meters +parameter["sa_inv_nmos_size"] = 0.27 # micro-meters +parameter["bitcell_drain_cap"] = 0.1 # In Femto-Farad, approximation of drain capacitance + +################################################### +# Technology Tool Preferences +################################################### + +if use_calibre: + drc_name = "calibre" + lvs_name = "calibre" + pex_name = "calibre" +elif use_klayout: + drc_name = "klayout" + lvs_name = "klayout" + pex_name = "klayout" +else: + drc_name = "magic" + lvs_name = "netgen" + pex_name = "magic" + + +# This is used by uniqify to not rename the library cells +library_prefix_name = "sky130_fd_bd_sram__" +# List of cells to skip running DRC/LVS on directly +# This will look for a maglef file and copy it over the mag file +# before DRC after extraction + +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_cell +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_ce +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_ce +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell +# gds flatglob sky130_fd_bd_sram__openram_sp_cell_opt1a_cell +# gds flatglob sky130_fd_bd_sram__sram_sp_cell_fom_serifs + +flatglob = ["*_?mos_m*", + "sky130_fd_bd_sram__sram_sp_cell_fom_serifs", + + "sky130_fd_bd_sram__sram_sp_cell", + "sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_cell", + + "sky130_fd_bd_sram__sram_sp_cell_opt1_ce", + "sky130_fd_bd_sram__openram_sp_cell_opt1_replica_ce", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap_p_ce"] + +blackbox_cells = ["sky130_fd_bd_sram__openram_dp_cell", + "sky130_fd_bd_sram__openram_dp_cell_dummy", + "sky130_fd_bd_sram__openram_dp_cell_replica", + + "sky130_fd_bd_sram__openram_sp_cell_opt1_noblcon", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_noblcon", + "sky130_fd_bd_sram__openram_sp_colend_replica", + "sky130_fd_bd_sram__openram_sp_colenda_replica", + "sky130_fd_bd_sram__sram_sp_cell_opt1a", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy", + "sky130_fd_bd_sram__openram_sp_cell_opt1_dummy", + "sky130_fd_bd_sram__sram_sp_cell_opt1_ce", + "sky130_fd_bd_sram__sram_sp_cell_opt1", + "sky130_fd_bd_sram__openram_sp_cell_opt1_replica", + "sky130_fd_bd_sram__openram_sp_cell_opt1a_replica", + "sky130_fd_bd_sram__sram_sp_colend", + "sky130_fd_bd_sram__sram_sp_colend_cent", + "sky130_fd_bd_sram__sram_sp_colend_p_cent", + "sky130_fd_bd_sram__sram_sp_colenda", + "sky130_fd_bd_sram__sram_sp_colenda_cent", + "sky130_fd_bd_sram__sram_sp_colenda_p_cent", + "sky130_fd_bd_sram__sram_sp_rowend", + "sky130_fd_bd_sram__sram_sp_rowenda", + "sky130_fd_bd_sram__openram_sp_rowend_replica", + "sky130_fd_bd_sram__openram_sp_rowenda_replica", + "sky130_fd_bd_sram__sram_sp_corner", + "sky130_fd_bd_sram__sram_sp_cornera", + "sky130_fd_bd_sram__sram_sp_cornerb", + "sky130_fd_bd_sram__sram_sp_wlstrapa", + "sky130_fd_bd_sram__sram_sp_wlstrap_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap", + "sky130_fd_bd_sram__sram_sp_wlstrap_p_ce", + "sky130_fd_bd_sram__sram_sp_wlstrap_p"] \ No newline at end of file