Files
OpenRAM/technology/sky130/tech/tech.py
T
ThVerg 1123f733bd sky130: use special_nfet_01v8 model for sub-0.42um nfets
The sky130 magic techfile splits nfet extraction on device width:

  device msubcircuit sky130_fd_pr__nfet_01v8 nfet,scnfet ... w>=0.42
  device msubcircuit sky130_fd_pr__special_nfet_01v8 scnfet ... w<0.42

ptx always emitted spice["nmos"], so every gate built from a
minimum-width device (drc["minwidth_tx"] = 0.36) was netlisted as
sky130_fd_pr__nfet_01v8 while magic extracted it as
sky130_fd_pr__special_nfet_01v8. netgen then reported a device class
mismatch in each such cell:

  Circuit 1: sky130_fd_pr__special_nfet_01v8 (1) | Circuit 2: (no matching element)
  Circuit 1: (no matching element)               | Circuit 2: sky130_fd_pr__nfet_01v8 (1)

The signed-off macros shipped in sky130A/libs.ref/sky130_sram_macros use
special_nfet_01v8 at this geometry, so this restores the naming those
were built with.

ptx now asks the technology for a narrow-device model, keyed by width.
Technologies that declare no such model are unaffected, and there is no
equivalent split for pfets so only the nmos is declared.

After this change pinv and pinv_dec report "Netlists match uniquely"
where they previously mismatched. Verified with ngspice that
special_nfet_01v8 resolves through sky130.lib.spice, so characterization
is unaffected.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01FcKSuKSchDEd7MqT8AffpN
2026-08-11 14:56:13 +02:00

850 lines
32 KiB
Python
Executable File

#!/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.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.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"
# An nfet narrower than 0.42um is a different device in sky130: magic
# extracts it as sky130_fd_pr__special_nfet_01v8, so the netlist has to use
# that name below the threshold or netgen reports a device class mismatch.
# See the "device msubcircuit" lines in sky130A.tech. There is no equivalent
# split for pfets, so only the nmos is declared here.
spice["nmos_narrow"] = "sky130_fd_pr__special_nfet_01v8"
spice["nmos_narrow_max_width"] = 0.42
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__sram_sp_cell_opt1a",
"sky130_fd_bd_sram__openram_sp_cell_opt1a_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"]