mirror of https://github.com/VLSIDA/OpenRAM.git
Merge b1c607f299 into ffb5074192
This commit is contained in:
commit
afc2ea11bb
|
|
@ -0,0 +1,41 @@
|
|||
"""
|
||||
This type of setup script should be placed in the setup_scripts directory in
|
||||
the trunk
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
TECHNOLOGY = "ihp_sg13g2"
|
||||
|
||||
os.environ["MGC_TMPDIR"] = "/tmp"
|
||||
|
||||
###########################
|
||||
# OpenRAM Paths
|
||||
|
||||
if "PDK_ROOT" in os.environ:
|
||||
open_pdks = os.path.join(os.environ["PDK_ROOT"], "ihp-sg13g2", "libs.tech")
|
||||
else:
|
||||
raise SystemError("Unable to find the IHP SG13G2 PDK. Set PDK_ROOT.")
|
||||
|
||||
# The ngspice models work with Xyce too now
|
||||
spice_model_dir = os.path.join(open_pdks, "ngspice", "models")
|
||||
ihp_lib_ngspice = os.path.join(open_pdks, "ngspice", ".spiceinit")
|
||||
if not os.path.exists(ihp_lib_ngspice):
|
||||
raise SystemError("Did not find {} under {}".format(ihp_lib_ngspice, open_pdks))
|
||||
os.environ["SPICE_MODEL_DIR"] = spice_model_dir
|
||||
|
||||
open_pdks = os.path.abspath(open_pdks)
|
||||
ihp_magicrc = os.path.join(open_pdks, 'magic', "ihp-sg13g2.magicrc")
|
||||
if not os.path.exists(ihp_magicrc):
|
||||
raise SystemError("Did not find {} under {}".format(ihp_magicrc, open_pdks))
|
||||
os.environ["OPENRAM_MAGICRC"] = ihp_magicrc
|
||||
ihp_netgenrc = os.path.join(open_pdks, 'netgen', "ihp-sg13g2_setup.tcl")
|
||||
if not os.path.exists(ihp_netgenrc):
|
||||
raise SystemError("Did not find {} under {}".format(ihp_netgenrc, open_pdks))
|
||||
os.environ["OPENRAM_NETGENRC"] = ihp_netgenrc
|
||||
|
||||
try:
|
||||
DRCLVS_HOME = os.path.abspath(os.environ.get("DRCLVS_HOME"))
|
||||
except:
|
||||
DRCLVS_HOME= "not-found"
|
||||
os.environ["DRCLVS_HOME"] = DRCLVS_HOME
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
"""
|
||||
Import tech specific modules.
|
||||
"""
|
||||
|
||||
from .tech import *
|
||||
|
|
@ -0,0 +1,528 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#=========================================================================================
|
||||
# Copyright 2025 IHP PDK Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#=========================================================================================
|
||||
|
||||
#====================================================================================================================
|
||||
#--------------------------------------------- IHP-SG13G2 DRC RULE DECK ---------------------------------------------
|
||||
#====================================================================================================================
|
||||
require 'time'
|
||||
require 'logger'
|
||||
require 'json'
|
||||
require 'pathname'
|
||||
require 'etc'
|
||||
|
||||
exec_start_time = Time.now
|
||||
|
||||
# Custom formatter including timestamp and memory usage
|
||||
formatter = proc do |_severity, datetime, _progname, msg|
|
||||
memory_usage = "#{RBA::Timer.memory_size / 1024}K"
|
||||
"#{datetime}: Memory Usage (#{memory_usage}) : #{msg}\n"
|
||||
end
|
||||
|
||||
# Create file logger
|
||||
file_logger = Logger.new($log)
|
||||
file_logger.formatter = formatter
|
||||
|
||||
# Create stdout logger
|
||||
stdout_logger = Logger.new($stdout)
|
||||
stdout_logger.formatter = formatter
|
||||
|
||||
# Don't buffer stdout
|
||||
$stdout.sync = true
|
||||
|
||||
# MultiLogger class to broadcast to multiple loggers
|
||||
class MultiLogger
|
||||
def initialize(*targets)
|
||||
@targets = targets
|
||||
end
|
||||
|
||||
def info(msg)
|
||||
@targets.each { |t| t.info(msg) }
|
||||
end
|
||||
|
||||
def warn(msg)
|
||||
@targets.each { |t| t.warn(msg) }
|
||||
end
|
||||
|
||||
def error(msg)
|
||||
@targets.each { |t| t.error(msg) }
|
||||
end
|
||||
end
|
||||
|
||||
# Use the multi-logger for your application
|
||||
logger = MultiLogger.new(stdout_logger, file_logger)
|
||||
|
||||
#================================================
|
||||
#----------------- FILE SETUP -------------------
|
||||
#================================================
|
||||
|
||||
logger.info("Starting running IHP-SG13G2 KLayout DRC runset on #{$input}")
|
||||
logger.info("Ruby Version for klayout: #{RUBY_VERSION}")
|
||||
|
||||
if $input
|
||||
if $topcell
|
||||
source($input, $topcell)
|
||||
else
|
||||
source($input)
|
||||
end
|
||||
end
|
||||
|
||||
logger.info('Loading database to memory is complete.')
|
||||
|
||||
if $report
|
||||
logger.info("IHP-SG13G2 KLayout DRC runset output at: #{$report}")
|
||||
report('Main DRC Run Report at', $report)
|
||||
else
|
||||
layout_dir = Pathname.new(RBA::CellView.active.filename).parent.realpath
|
||||
report_path = layout_dir.join('sg13g2_drc_main.lyrdb').to_s
|
||||
logger.info("IHP-SG13G2 KLayout DRC runset output at default location: #{report_path}")
|
||||
report('Main DRC Run Report at', report_path)
|
||||
end
|
||||
|
||||
#================================================
|
||||
#------------------ SWITCHES --------------------
|
||||
#================================================
|
||||
|
||||
logger.info('Evaluate switches.')
|
||||
|
||||
def bool_check?(obj)
|
||||
obj.to_s.downcase == 'true'
|
||||
end
|
||||
|
||||
TABLES = ($tables || 'main').split
|
||||
logger.info("tables selected: #{TABLES.join(' ')}")
|
||||
|
||||
# Run mode setup
|
||||
def setup_run_mode(run_mode)
|
||||
case run_mode
|
||||
when 'tiling'
|
||||
tiles(500.um)
|
||||
tile_borders(30.um)
|
||||
when 'flat'
|
||||
flat
|
||||
else
|
||||
deep
|
||||
end
|
||||
end
|
||||
|
||||
# Threads
|
||||
$threads = ($threads || Etc.nprocessors).to_i
|
||||
threads($threads)
|
||||
logger.info("KLayout will use #{$threads} thread(s) in tiling mode.")
|
||||
|
||||
# Run mode
|
||||
$run_mode = $run_mode || 'deep'
|
||||
setup_run_mode($run_mode)
|
||||
logger.info("#{$run_mode} mode is enabled.")
|
||||
|
||||
# FEOL
|
||||
FEOL = !bool_check?($no_feol)
|
||||
logger.info("FEOL enabled: #{FEOL}")
|
||||
|
||||
# BEOL
|
||||
BEOL = !bool_check?($no_beol)
|
||||
logger.info("BEOL enabled: #{BEOL}")
|
||||
|
||||
# OFFGRID
|
||||
OFFGRID = !bool_check?($no_offgrid)
|
||||
logger.info("OFFGRID enabled: #{OFFGRID}")
|
||||
|
||||
# ANGLE
|
||||
ANGLE = !bool_check?($no_angle)
|
||||
logger.info("ANGLE enabled: #{ANGLE}")
|
||||
|
||||
# PIN
|
||||
PIN = !bool_check?($no_pin)
|
||||
logger.info("PIN enabled: #{PIN}")
|
||||
|
||||
# FORBIDDEN
|
||||
FORBIDDEN = !bool_check?($no_forbidden)
|
||||
logger.info("FORBIDDEN enabled: #{FORBIDDEN}")
|
||||
|
||||
# RECOMMENDED
|
||||
RECOMMENDED = !bool_check?($no_recommended)
|
||||
logger.info("RECOMMENDED enabled: #{RECOMMENDED}")
|
||||
|
||||
# PreCheck DRC
|
||||
PRECHECK_DRC = bool_check?($precheck_drc)
|
||||
logger.info("PreCheck DRC enabled: #{PRECHECK_DRC}")
|
||||
|
||||
# Connectivity rules
|
||||
# Needed for full FEOL runs and for explicit nwell/nbulay table runs.
|
||||
conn_tables = %w[nwell nbulay]
|
||||
CONNECTIVITY_RULES = FEOL || TABLES.any?{|x| conn_tables.include?(x)}
|
||||
logger.info("CONNECTIVITY_RULES enabled: #{CONNECTIVITY_RULES}.")
|
||||
|
||||
#================================================
|
||||
#------------- LAYERS DEFINITIONS ---------------
|
||||
#================================================
|
||||
|
||||
# %include rule_decks/layers_def.drc
|
||||
|
||||
#================================================
|
||||
# -------------------- UTILS --------------------
|
||||
#================================================
|
||||
|
||||
# Method to get DRC values from JSON files
|
||||
def get_drc_values(logger)
|
||||
script_dir = File.expand_path(File.dirname(__FILE__))
|
||||
$drc_json_default = $drc_json_default || File.expand_path(File.join(script_dir, 'rule_decks/sg13g2_tech_default.json'))
|
||||
$drc_json = $drc_json || File.expand_path(File.join(script_dir, '../../python/sg13g2_pycell_lib/sg13g2_tech_mod.json'))
|
||||
|
||||
tech_rules = {}
|
||||
if $drc_json && $drc_json != $drc_json_default
|
||||
begin
|
||||
tech_drc_content = File.read($drc_json)
|
||||
tech_drc_data = JSON.parse(tech_drc_content)
|
||||
tech_rules = tech_drc_data['drc_rules'] || {}
|
||||
logger.info("Loaded TECH DRC rules values from #{$drc_json}")
|
||||
rescue StandardError => e
|
||||
logger.error("Error reading TECH DRC rules from #{$drc_json}: #{e.message}")
|
||||
raise "Error reading TECH DRC rules from #{$drc_json}: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
default_drc_content = File.read($drc_json_default)
|
||||
default_drc_data = JSON.parse(default_drc_content)
|
||||
default_rules = default_drc_data['drc_rules'] || {}
|
||||
logger.info("Loaded default DRC rules values from #{$drc_json_default}")
|
||||
rescue StandardError => e
|
||||
logger.error("Error reading default DRC rules from #{$drc_json_default}: #{e.message}")
|
||||
raise "Error reading default DRC rules from #{$drc_json_default}: #{e.message}"
|
||||
end
|
||||
|
||||
merged_rules = tech_rules.merge(default_rules)
|
||||
|
||||
# Report any fallback rules used
|
||||
missing_keys = default_rules.keys - tech_rules.keys
|
||||
unless missing_keys.empty?
|
||||
logger.warn('The following rules values were missing in tech json and default values were used:')
|
||||
missing_keys.each { |rule| logger.warn(" - #{rule}") }
|
||||
end
|
||||
|
||||
merged_rules
|
||||
end
|
||||
|
||||
# Extract DRC rules values from the JSON file
|
||||
drc_rules = get_drc_values(logger)
|
||||
|
||||
# Method to convert glob pattern to a case-insensitive glob-style pattern
|
||||
def glob_to_case_insensitive_glob(glob)
|
||||
wildcards = ['*', '?']
|
||||
|
||||
pattern = glob.chars.map do |c|
|
||||
if c =~ /[A-Za-z]/
|
||||
"[#{c.upcase}#{c.downcase}]"
|
||||
elsif wildcards.include?(c)
|
||||
c # Keep wildcard characters as they are
|
||||
else
|
||||
Regexp.escape(c)
|
||||
end
|
||||
end.join
|
||||
|
||||
pattern
|
||||
end
|
||||
|
||||
#================================================
|
||||
#-------------- COMMON DERIVATIONS --------------
|
||||
#================================================
|
||||
|
||||
# === LAYOUT EXTENT ===
|
||||
CHIP = extent.sized(0.0)
|
||||
chip_area = CHIP.area
|
||||
chip_bbox = CHIP.bbox
|
||||
chip_w = chip_bbox.width
|
||||
chip_l = chip_bbox.height
|
||||
|
||||
logger.info("Total area of the design is #{chip_area} um^2.")
|
||||
logger.info("The design dimensions are #{chip_w} µm * #{chip_l} µm.")
|
||||
|
||||
# Enable tiling if chip area exceeds 100,000 µm²
|
||||
en_tiles = chip_area > 1_000_000.um2
|
||||
|
||||
# === General Derivations ===
|
||||
logger.info('Starting general IHP-SG13G2 derivations.')
|
||||
|
||||
# Pwell
|
||||
logger.info('Starting Pwell derivations')
|
||||
pwell_allowed = CHIP.not(pwell_block)
|
||||
digisub_gap = digisub_drw.not(digisub_drw.sized(-1.nm))
|
||||
pwell = pwell_allowed.not(nwell_drw).not(digisub_gap)
|
||||
|
||||
# nBulay
|
||||
nbulay_tables = %w[main nbulay nwell activfiller schottkydiode]
|
||||
if TABLES.any?{|x| nbulay_tables.include?(x)}
|
||||
nbulay_gen_sized = nwell_drw.sized(-1.495.um).sized(0.495.um)
|
||||
nbuLay_gen = nbulay_gen_sized.not(nbulay_block.join(res_drw))
|
||||
nbuLay_gen_nbulay = nbuLay_gen.join(nbulay_drw)
|
||||
end
|
||||
|
||||
# n activ
|
||||
nact_pact_tables = %w[main pwellblock gatpoly cont latchup nwell nbulay]
|
||||
if TABLES.any?{|x| nact_pact_tables.include?(x)}
|
||||
logger.info('Starting n-Activ derivations')
|
||||
nactiv = activ_drw.not(psd_drw.join(nsd_block))
|
||||
end
|
||||
|
||||
# p activ
|
||||
pact_pact_tables = %w[main pwellblock gatpoly cont npnsubstratetie latchup nwell nbulay schottkydiode contbar via1]
|
||||
if TABLES.any?{|x| pact_pact_tables.include?(x)}
|
||||
logger.info('Starting p-Activ derivations')
|
||||
pactiv = activ_drw.and(psd_drw)
|
||||
end
|
||||
|
||||
# p tap
|
||||
ptap_tables = %w[main pwellblock npnsubstratetie latchup nwell nbulay schottkydiode contbar via1]
|
||||
if TABLES.any?{|x| ptap_tables.include?(x)}
|
||||
logger.info('Starting ptap derivations')
|
||||
ptap = pactiv.and(pwell)
|
||||
end
|
||||
|
||||
# n tap
|
||||
ntap_tables = %w[main nwell nbulay]
|
||||
if TABLES.any?{|x| ntap_tables.include?(x)}
|
||||
logger.info('Starting ntap derivations')
|
||||
ntap = nactiv.and(nwell_drw)
|
||||
end
|
||||
|
||||
# activ FETs
|
||||
pact_fet_tables = %w[main gatpoly nwell nbulay]
|
||||
if TABLES.any?{|x| pact_fet_tables.include?(x)}
|
||||
logger.info('Starting pact FET derivations')
|
||||
pact_fet = pactiv.and(nwell_drw)
|
||||
end
|
||||
|
||||
nact_fet_tables = %w[main pwellblock gatpoly latchup nwell nbulay cont]
|
||||
if TABLES.any?{|x| nact_fet_tables.include?(x)}
|
||||
logger.info('Starting nact FET derivations')
|
||||
nact_fet = nactiv.and(pwell)
|
||||
end
|
||||
|
||||
# Gate FETs
|
||||
gate_tables = %w[main gatpoly nwell nbulay]
|
||||
if TABLES.any?{|x| gate_tables.include?(x)}
|
||||
logger.info('Starting Gate derivations')
|
||||
res_mk = polyres_drw.join(res_drw)
|
||||
ngate = nact_fet.and(gatpoly_drw)
|
||||
pgate = pact_fet.and(gatpoly_drw)
|
||||
end
|
||||
|
||||
# Cont derivations
|
||||
cont_tables = %w[main cont contbar schottkydiode]
|
||||
if TABLES.any?{|x| cont_tables.include?(x)}
|
||||
logger.info('Starting cont/contbar general derivations')
|
||||
cont_nseal = cont_drw.not(edgeseal_drw)
|
||||
contbar = cont_nseal.non_squares
|
||||
cont_sq = cont_nseal.not(contbar)
|
||||
cont_nseal.forget
|
||||
end
|
||||
|
||||
# Pad derivations
|
||||
pad_tables = %w[main pad solderbump copperpillar]
|
||||
if TABLES.any?{|x| pad_tables.include?(x)}
|
||||
logger.info('Starting pad general derivations')
|
||||
cu_pillarpad = passiv_pillar.and(dfpad_pillar).and(topmetal2_drw)
|
||||
sbumppad = dfpad_sbump.and(passiv_sbump).and(topmetal2_drw)
|
||||
pad = passiv_drw.and(dfpad_drw).and(topmetal2_drw)
|
||||
end
|
||||
|
||||
# schottky_nbl derivations
|
||||
schottky_tables = %w[main schottkydiode pwellblock]
|
||||
if TABLES.any?{|x| schottky_tables.include?(x)}
|
||||
logger.info('Starting schottky_nbl general derivations')
|
||||
schottky_nbl_mk = recog_diode.and(thickgateox_drw).and(salblock_drw).and(nsd_block)
|
||||
schottky_nbl_rec = nbulay_drw.not(nwell_drw).not_outside(schottky_nbl_mk)
|
||||
schottky_nbl1 = ptap.holes.covering(schottky_nbl_rec)
|
||||
end
|
||||
|
||||
# SVaricap derivations
|
||||
svaricap_tables = %w[main cont nbulay nwell]
|
||||
if TABLES.any?{|x| svaricap_tables.include?(x)}
|
||||
logger.info('Starting SVaricap general derivations')
|
||||
svaricap_gate = gatpoly_drw.and(nwell_drw).and(nbulay_drw)
|
||||
svaricap_patt = glob_to_case_insensitive_glob("SVaricap")
|
||||
svaricap_text = text_drw.texts(svaricap_patt)
|
||||
svaricap_recog = activ_drw.not_outside(svaricap_gate).interacting(svaricap_text)
|
||||
svaricap = nwell_drw.not_outside(svaricap_recog)
|
||||
end
|
||||
|
||||
# HBT derivations
|
||||
npn_tables = %w[main npnsubstratetie contbar via1]
|
||||
if TABLES.any?{|x| npn_tables.include?(x)}
|
||||
logger.info('Starting HBT general derivations')
|
||||
sg13g2_patt = glob_to_case_insensitive_glob("npn13G2")
|
||||
sg13g2l_patt = glob_to_case_insensitive_glob("npn13G2L")
|
||||
sg13g2v_patt = glob_to_case_insensitive_glob("npn13G2V")
|
||||
npn_mk = trans_drw.and(ptap.holes)
|
||||
npn13g2 = npn_mk.interacting(text_drw.texts(sg13g2_patt)).covering(emwind_drw.and(activ_mask).and(nsd_block))
|
||||
npn13g2l = npn_mk.interacting(text_drw.texts(sg13g2l_patt)).covering(emwind_drw.and(activ_drw))
|
||||
npn13g2v = npn_mk.interacting(text_drw.texts(sg13g2v_patt)).covering(emwihv_drw.and(activ_drw))
|
||||
end
|
||||
|
||||
# DRC tolerance value
|
||||
drc_tole = 0.005.um
|
||||
|
||||
#================================================
|
||||
#------------- LAYERS CONNECTIONS ---------------
|
||||
#================================================
|
||||
|
||||
if CONNECTIVITY_RULES
|
||||
logger.info('Starting IHP-SG13G2 DRC connectivity related derivations.')
|
||||
|
||||
# Wells
|
||||
nwell_ring_niso = nwell_drw.with_holes.not(nbulay_drw)
|
||||
nwell_holes = nwell_drw.holes.not(nwell_drw)
|
||||
isoPWell = nbulay_drw.and(nwell_holes)
|
||||
pwell_sub = pwell.not(isoPWell).join(nwell_ring_niso)
|
||||
|
||||
# conn layers
|
||||
poly_con = gatpoly_drw.not(res_mk)
|
||||
metal1_con = metal1_drw.not(metal1_res)
|
||||
metal2_con = metal2_drw.not(metal2_res)
|
||||
metal3_con = metal3_drw.not(metal3_res)
|
||||
metal4_con = metal4_drw.not(metal4_res)
|
||||
metal5_con = metal5_drw.not(metal5_res)
|
||||
topmetal1_con = topmetal1_drw.not(topmetal1_res).not(ind_drw)
|
||||
topmetal2_con = topmetal2_drw.not(topmetal2_res).not(ind_drw)
|
||||
|
||||
# Mimcap
|
||||
mim_via = vmim_drw.join(topvia1_drw).and(mim_drw)
|
||||
topvia1_n_cap = topvia1_drw.not(mim_via)
|
||||
|
||||
logger.info('Starting IHP-SG13G2 DRC connectivity setup')
|
||||
# Inter-layer
|
||||
connect(pwell_sub, pwell)
|
||||
connect(pwell, ptap)
|
||||
connect(nwell_drw, ntap)
|
||||
connect(cont_drw, nactiv)
|
||||
connect(cont_drw, pactiv)
|
||||
connect(nbulay_drw, nbuLay_gen_nbulay)
|
||||
connect(nbuLay_gen_nbulay, nwell_drw)
|
||||
connect(ntap, cont_drw)
|
||||
connect(ptap, cont_drw)
|
||||
connect(poly_con, cont_drw)
|
||||
connect(nact_fet, cont_drw)
|
||||
connect(pact_fet, cont_drw)
|
||||
connect(cont_drw, metal1_con)
|
||||
connect(metal1_con, via1_drw)
|
||||
connect(via1_drw, metal2_con)
|
||||
connect(metal2_con, via2_drw)
|
||||
connect(via2_drw, metal3_con)
|
||||
connect(metal3_con, via3_drw)
|
||||
connect(via3_drw, metal4_con)
|
||||
connect(metal4_con, via4_drw)
|
||||
connect(via4_drw, metal5_con)
|
||||
connect(metal5_con, topvia1_n_cap)
|
||||
connect(topvia1_n_cap, topmetal1_con)
|
||||
connect(topmetal1_con, topvia2_drw)
|
||||
connect(topvia2_drw, topmetal2_con)
|
||||
|
||||
# nwell nets
|
||||
nwell_nets = nwell_drw.nets
|
||||
end
|
||||
|
||||
#================================================
|
||||
#------------ PRE-DEFINED FUNCTIONS -------------
|
||||
#================================================
|
||||
|
||||
def get_circle(polygon_check)
|
||||
maybe_circle1 = polygon_check.with_bbox_aspect_ratio(1)
|
||||
maybe_circle2 = maybe_circle1.select { |polygon| polygon.num_points >= 16 }
|
||||
# Circle detection based on area ratio of bounding box to shape area ≈ 4/π ≈ 1.2732
|
||||
# (Derived from: (d^2) / (π * d^2 / 4) = 4/π)
|
||||
maybe_circle2.without_holes.with_area_ratio(1.270, 1.276)
|
||||
end
|
||||
|
||||
def get_octagon(polygon_check)
|
||||
maybe_oct1 = polygon_check.with_bbox_aspect_ratio(1)
|
||||
maybe_oct2 = maybe_oct1.select { |polygon| polygon.num_points == 8 }
|
||||
h_edges_check = maybe_oct2.edges.with_angle(0, absolute)
|
||||
maybe_oct3 = maybe_oct2.interacting(h_edges_check, 2, 2)
|
||||
v_edges_check = maybe_oct3.edges.with_angle(90, absolute)
|
||||
maybe_oct4 = maybe_oct3.interacting(v_edges_check, 2, 2)
|
||||
diag_edges_check = maybe_oct4.edges.with_angle(44.5, 45.5, absolute)
|
||||
maybe_oct4.interacting(diag_edges_check, 4, 4)
|
||||
end
|
||||
|
||||
#================================================
|
||||
#----------------- MAIN RUNSET ------------------
|
||||
#================================================
|
||||
|
||||
logger.info('Starting IHP-SG13G2 DRC rules.')
|
||||
|
||||
# === FEOL ===
|
||||
logger.info('Running all FEOL rules') if FEOL
|
||||
# === BEOL ===
|
||||
logger.info('Running all BEOL rules') if BEOL
|
||||
# === PIN ===
|
||||
logger.info('Running all PIN rules') if PIN
|
||||
# === FORBIDDEN ===
|
||||
logger.info('Running all FORBIDDEN rules') if FORBIDDEN
|
||||
|
||||
#================================================
|
||||
#------------------- INCLUDES -------------------
|
||||
#================================================
|
||||
|
||||
# %include rule_decks/feol/5_1_nwell.drc
|
||||
# %include rule_decks/feol/5_2_pwellblock.drc
|
||||
# %include rule_decks/feol/5_3_nbulay.drc
|
||||
# %include rule_decks/feol/5_5_activ.drc
|
||||
# %include rule_decks/feol/5_6_activfiller.drc
|
||||
# %include rule_decks/feol/5_7_thickgateox.drc
|
||||
# %include rule_decks/feol/5_8_gatpoly.drc
|
||||
# %include rule_decks/feol/5_9_gatpolyfiller.drc
|
||||
# %include rule_decks/feol/5_10_psd.drc
|
||||
# %include rule_decks/feol/5_14_cont.drc
|
||||
# %include rule_decks/feol/5_15_contbar.drc
|
||||
# %include rule_decks/feol/6_1_npnsubstratetie.drc
|
||||
# %include rule_decks/feol/6_7_schottkydiode.drc
|
||||
# %include rule_decks/feol/7_2_latchup.drc
|
||||
|
||||
# %include rule_decks/beol/5_16_metal1.drc
|
||||
# %include rule_decks/beol/5_17_metaln.drc
|
||||
# %include rule_decks/beol/5_18_metalnfiller.drc
|
||||
# %include rule_decks/beol/5_19_via1.drc
|
||||
# %include rule_decks/beol/5_20_vian.drc
|
||||
# %include rule_decks/beol/5_21_topvia1.drc
|
||||
# %include rule_decks/beol/5_22_topmetal1.drc
|
||||
# %include rule_decks/beol/5_23_topmetal1filler.drc
|
||||
# %include rule_decks/beol/5_24_topvia2.drc
|
||||
# %include rule_decks/beol/5_25_topmetal2.drc
|
||||
# %include rule_decks/beol/5_26_topmetal2filler.drc
|
||||
# %include rule_decks/beol/5_27_passiv.drc
|
||||
# %include rule_decks/beol/6_9_copperpillar.drc
|
||||
# %include rule_decks/beol/6_9_pad.drc
|
||||
# %include rule_decks/beol/6_9_solderbump.drc
|
||||
# %include rule_decks/beol/6_10_sealring.drc
|
||||
# %include rule_decks/beol/6_11_mim.drc
|
||||
# %include rule_decks/beol/7_3_metalslits.drc
|
||||
# %include rule_decks/beol/9_1_lbe.drc
|
||||
|
||||
# %include rule_decks/pin/7_4_pin.drc
|
||||
|
||||
# %include rule_decks/geometry/3_1_offgrid.drc
|
||||
# %include rule_decks/geometry/3_2_angle.drc
|
||||
# %include rule_decks/forbidden/3_2_forbidden.drc
|
||||
|
||||
#================================================
|
||||
#--------------------- TAIL ---------------------
|
||||
#================================================
|
||||
|
||||
exec_end_time = Time.now
|
||||
run_time = exec_end_time - exec_start_time
|
||||
logger.info("KLayout DRC run for tables '#{TABLES.join(' ')}' completed in #{run_time.round(2)} seconds")
|
||||
|
|
@ -0,0 +1,513 @@
|
|||
#==========================================================================
|
||||
# Copyright 2024 IHP PDK Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#==========================================================================
|
||||
|
||||
#===========================================================================================================
|
||||
# ------------------------------------------ SG13G2 LVS RULE DECK ------------------------------------------
|
||||
#===========================================================================================================
|
||||
|
||||
require 'time'
|
||||
require 'logger'
|
||||
require 'etc'
|
||||
|
||||
exec_start_time = Time.now
|
||||
|
||||
# Custom formatter including timestamp and memory usage
|
||||
formatter = proc do |_severity, datetime, _progname, msg|
|
||||
memory_usage = "#{RBA::Timer.memory_size / 1024}K"
|
||||
"#{datetime}: Memory Usage (#{memory_usage}) : #{msg}\n"
|
||||
end
|
||||
|
||||
# Create file logger
|
||||
file_logger = Logger.new($log)
|
||||
file_logger.formatter = formatter
|
||||
|
||||
# Create stdout logger
|
||||
stdout_logger = Logger.new($stdout)
|
||||
stdout_logger.formatter = formatter
|
||||
|
||||
# MultiLogger class to broadcast to multiple loggers
|
||||
class MultiLogger
|
||||
def initialize(*targets)
|
||||
@targets = targets
|
||||
end
|
||||
|
||||
def info(msg)
|
||||
@targets.each { |t| t.info(msg) }
|
||||
end
|
||||
|
||||
def warn(msg)
|
||||
@targets.each { |t| t.warn(msg) }
|
||||
end
|
||||
|
||||
def error(msg)
|
||||
@targets.each { |t| t.error(msg) }
|
||||
end
|
||||
end
|
||||
|
||||
# Use the multi-logger for your application
|
||||
logger = MultiLogger.new(stdout_logger, file_logger)
|
||||
|
||||
#================================================
|
||||
#----------------- FILE SETUP -------------------
|
||||
#================================================
|
||||
|
||||
logger.info("Starting running SG13G2 Klayout LVS runset on #{$input}")
|
||||
logger.info("Ruby Version for klayout: #{RUBY_VERSION}")
|
||||
|
||||
if $input
|
||||
if $topcell
|
||||
src = source($input, $topcell)
|
||||
else
|
||||
src = source($input)
|
||||
end
|
||||
end
|
||||
|
||||
logger.info('Loading database to memory is complete.')
|
||||
|
||||
if $report
|
||||
logger.info("SG13G2 Klayout LVS runset output at: #{$report}")
|
||||
report_lvs($report)
|
||||
else
|
||||
layout_dir = Pathname.new(RBA::CellView.active.filename).parent.realpath
|
||||
report_path = layout_dir.join("#{source.cell_name}.lvsdb").to_s
|
||||
logger.info("SG13G2 Klayout LVS runset output at default location: #{source.cell_name}.lvsdb")
|
||||
report_lvs($report_path)
|
||||
end
|
||||
|
||||
#================================================
|
||||
#---------------- SRAM SUPPORT ------------------
|
||||
#================================================
|
||||
|
||||
# %include rule_decks/sram_integration.lvs
|
||||
|
||||
#================================================
|
||||
#------------------ SWITCHES --------------------
|
||||
#================================================
|
||||
|
||||
logger.info('Evaluate switches.')
|
||||
|
||||
def bool_check?(obj)
|
||||
obj.to_s.downcase == 'true'
|
||||
end
|
||||
|
||||
#=== NET NAMES OPTION ===
|
||||
# true: use net names instead of numbers
|
||||
# false: use numbers for nets
|
||||
SPICE_WITH_NET_NAMES = !bool_check?($no_net_names)
|
||||
|
||||
logger.info("Extracted netlist with net names: #{SPICE_WITH_NET_NAMES}")
|
||||
|
||||
#=== COMMENTS OPTION ===
|
||||
# true: put in comments with details
|
||||
# false: no comments
|
||||
SPICE_WITH_COMMENTS = bool_check?($spice_comments)
|
||||
|
||||
logger.info("Extracted netlist with comments in details: #{SPICE_WITH_COMMENTS}")
|
||||
|
||||
# NET_ONLY
|
||||
NET_ONLY = bool_check?($net_only)
|
||||
|
||||
logger.info("Selected NET_ONLY option: #{NET_ONLY}")
|
||||
|
||||
# LAYOUT_NETLIST
|
||||
LAYOUT_NETLIST_PATH = ($layout_netlist || '').to_s.strip
|
||||
LAYOUT_NETLIST = !LAYOUT_NETLIST_PATH.empty?
|
||||
|
||||
logger.info("Selected LAYOUT_NETLIST option: #{LAYOUT_NETLIST ? LAYOUT_NETLIST_PATH : '(none)'}")
|
||||
|
||||
# IGNORE_TOP_PORTS_MISMATCH
|
||||
floating_bulk_sram_top = src && floating_bulk_sram_cell?(src.cell_name)
|
||||
IGNORE_TOP_PORTS_MISMATCH = bool_check?($ignore_top_ports_mismatch) || floating_bulk_sram_top
|
||||
|
||||
logger.info("Selected IGNORE_TOP_PORTS_MISMATCH option: #{IGNORE_TOP_PORTS_MISMATCH}")
|
||||
if floating_bulk_sram_top && !bool_check?($ignore_top_ports_mismatch)
|
||||
logger.info("Floating-bulk SRAM top cell '#{src.cell_name}' detected: enabling IGNORE_TOP_PORTS_MISMATCH.")
|
||||
end
|
||||
|
||||
# IMPLICIT_NETS
|
||||
IMPLICIT_NETS = ($implicit_nets || '').to_s.strip
|
||||
|
||||
logger.info("Selected IMPLICIT_NETS option: #{IMPLICIT_NETS.empty? ? '(none)' : IMPLICIT_NETS}")
|
||||
|
||||
# TOP_LVL_PINS
|
||||
TOP_LVL_PINS = bool_check?($top_lvl_pins)
|
||||
|
||||
logger.info("Selected TOP_LVL_PINS option: #{TOP_LVL_PINS}")
|
||||
|
||||
# COMBINE
|
||||
COMBINE_DEVICES = bool_check?($combine_devices)
|
||||
|
||||
logger.info("Selected COMBINE DEVICES option: #{COMBINE_DEVICES}")
|
||||
|
||||
# PURGE
|
||||
PURGE = bool_check?($purge)
|
||||
|
||||
logger.info("Selected PURGE option: #{PURGE}")
|
||||
|
||||
# PURGE_NETS
|
||||
PURGE_NETS = bool_check?($purge_nets)
|
||||
|
||||
logger.info("Selected PURGE_NETS option: #{PURGE_NETS}")
|
||||
|
||||
# SIMPLIFY
|
||||
SIMPLIFY = !bool_check?($no_simplify)
|
||||
|
||||
logger.info("Selected SIMPLIFY option: #{SIMPLIFY}")
|
||||
|
||||
# SIMPLIFY
|
||||
SERIES_RES = !bool_check?($no_series_res)
|
||||
|
||||
logger.info("Selected SERIES_RES option: #{SERIES_RES}")
|
||||
|
||||
# SIMPLIFY
|
||||
PARALLEL_RES = !bool_check?($no_parallel_res)
|
||||
|
||||
logger.info("Selected PARALLEL_RES option: #{PARALLEL_RES}")
|
||||
|
||||
# === RUN MODE ===
|
||||
case $run_mode
|
||||
when 'deep'
|
||||
#=== HIER MODE ===
|
||||
deep
|
||||
logger.info('deep mode is enabled.')
|
||||
else
|
||||
#=== FLAT MODE ===
|
||||
flat
|
||||
logger.info('flat mode is enabled.')
|
||||
end
|
||||
|
||||
# === Tech Switches ===
|
||||
|
||||
#==================================================
|
||||
# --------------- IMPLICIT CONNECTIONS -------------
|
||||
#==================================================
|
||||
# KLayout LVS docs:
|
||||
# https://www.klayout.de/doc/manual/lvs.html
|
||||
# "connect_implicit" creates a virtual combined physical net
|
||||
# that is matched against the corresponding logical net name.
|
||||
if !IMPLICIT_NETS.empty?
|
||||
nets_to_connect = IMPLICIT_NETS.split(',').map(&:strip).reject(&:empty?)
|
||||
if nets_to_connect.empty?
|
||||
logger.info('WARNING : IMPLICIT_NETS was set but no valid entries were found after parsing.')
|
||||
else
|
||||
logger.info(
|
||||
"Applying implicit connections (case-sensitive, pattern supported) for nets: #{nets_to_connect.join(', ')}"
|
||||
)
|
||||
nets_to_connect.each do |net_pattern|
|
||||
connect_implicit(net_pattern)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#================================================
|
||||
# --------------- CUSTOM CLASSES ----------------
|
||||
#================================================
|
||||
|
||||
# %include rule_decks/custom_classes.lvs
|
||||
|
||||
# Instantiate a reader using the new delegate
|
||||
reader = RBA::NetlistSpiceReader.new(CustomReader.new)
|
||||
|
||||
#=== GET NETLIST ===
|
||||
unless NET_ONLY
|
||||
if $schematic
|
||||
schematic($schematic, reader)
|
||||
logger.info("Netlist file: #{$schematic}")
|
||||
else
|
||||
exts = %w[spice cdl cir]
|
||||
candidates = exts.map { |ext| "#{source.cell_name}.#{ext}" }
|
||||
netlists = candidates.select { |f| File.exist?(f) }
|
||||
if netlists.empty?
|
||||
error("Netlist not found, tried: #{candidates}")
|
||||
else
|
||||
schematic(netlists[0], reader)
|
||||
logger.info("Netlist file: #{netlists[0]}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Instantiate a writer using the new delegate
|
||||
custom_spice_writer = RBA::NetlistSpiceWriter.new(CustomWriter.new)
|
||||
custom_spice_writer.use_net_names = SPICE_WITH_NET_NAMES
|
||||
custom_spice_writer.with_comments = SPICE_WITH_COMMENTS
|
||||
|
||||
if $target_netlist
|
||||
logger.info("LVS extracted netlist at: #{$target_netlist}")
|
||||
target_netlist($target_netlist, custom_spice_writer,
|
||||
"Extracted by KLayout with SG13G2 LVS runset on : #{Time.now.strftime('%d/%m/%Y %H:%M')}")
|
||||
else
|
||||
layout_dir = Pathname.new(RBA::CellView.active.filename).parent.realpath
|
||||
netlist_path = layout_dir.join("#{source.cell_name}_extracted.cir")
|
||||
target_netlist(netlist_path.to_s, custom_spice_writer,
|
||||
"Extracted by KLayout with SG13G2 LVS runset on : #{Time.now.strftime('%d/%m/%Y %H:%M')}")
|
||||
logger.info("SG13G2 Klayout LVS extracted netlist file at: #{source.cell_name}_extracted.cir")
|
||||
end
|
||||
|
||||
logger.info('Applying SRAM integration support.')
|
||||
apply_sram_integration
|
||||
|
||||
#================================================================
|
||||
#------------------------- MAIN RUNSET --------------------------
|
||||
#================================================================
|
||||
|
||||
logger.info('Starting SG13G2 LVS runset')
|
||||
|
||||
if LAYOUT_NETLIST
|
||||
logger.info('LAYOUT_NETLIST enabled: skipping derivations/connectivity/extraction and loading layout netlist from file.')
|
||||
unless File.file?(LAYOUT_NETLIST_PATH)
|
||||
error("LAYOUT_NETLIST file does not exist: #{LAYOUT_NETLIST_PATH}")
|
||||
end
|
||||
|
||||
begin
|
||||
netlist.read(LAYOUT_NETLIST_PATH, reader)
|
||||
logger.info("Layout-side netlist file: #{LAYOUT_NETLIST_PATH}")
|
||||
rescue StandardError => e
|
||||
error("Failed to read LAYOUT_NETLIST '#{LAYOUT_NETLIST_PATH}': #{e.message}")
|
||||
end
|
||||
else
|
||||
#================================================
|
||||
#------------- LAYERS DEFINITIONS ---------------
|
||||
#================================================
|
||||
|
||||
# %include rule_decks/layers_definitions.lvs
|
||||
|
||||
#================================================
|
||||
#------------- LAYERS DERIVATIONS ---------------
|
||||
#================================================
|
||||
|
||||
logger.info('Starting base layers derivations')
|
||||
|
||||
#==================================
|
||||
# ------ GENERAL DERIVATIONS ------
|
||||
#==================================
|
||||
|
||||
# %include rule_decks/general_derivations.lvs
|
||||
|
||||
#==================================
|
||||
# ------ MOSFET DERIVATIONS -------
|
||||
#==================================
|
||||
|
||||
# %include rule_decks/mos_derivations.lvs
|
||||
|
||||
#==================================
|
||||
# ----- RF-MOSFET DERIVATIONS -----
|
||||
#==================================
|
||||
|
||||
# %include rule_decks/rfmos_derivations.lvs
|
||||
|
||||
#================================
|
||||
# ------ BJT DERIVATIONS --------
|
||||
#================================
|
||||
|
||||
# %include rule_decks/bjt_derivations.lvs
|
||||
|
||||
#================================
|
||||
# ----- DIODE DERIVATIONS -------
|
||||
#================================
|
||||
|
||||
# %include rule_decks/diode_derivations.lvs
|
||||
|
||||
#================================
|
||||
# ---- RESISTOR DERIVATIONS -----
|
||||
#================================
|
||||
|
||||
# %include rule_decks/res_derivations.lvs
|
||||
|
||||
#==================================
|
||||
# -------- CAP DERIVATIONS --------
|
||||
#==================================
|
||||
|
||||
# %include rule_decks/cap_derivations.lvs
|
||||
|
||||
#================================
|
||||
# ------ ESD DERIVATIONS --------
|
||||
#================================
|
||||
|
||||
# %include rule_decks/esd_derivations.lvs
|
||||
|
||||
#=================================
|
||||
# ----- Inductor DERIVATIONS -----
|
||||
#=================================
|
||||
|
||||
# %include rule_decks/ind_derivations.lvs
|
||||
|
||||
#================================
|
||||
# ------ Taps DERIVATIONS -------
|
||||
#================================
|
||||
|
||||
# %include rule_decks/tap_derivations.lvs
|
||||
|
||||
#================================================
|
||||
#------------ DEVICES CONNECTIVITY --------------
|
||||
#================================================
|
||||
|
||||
# %include rule_decks/devices_connections.lvs
|
||||
|
||||
#================================================
|
||||
#------------- DEVICES EXTRACTION ---------------
|
||||
#================================================
|
||||
|
||||
logger.info('Starting SG13G2 LVS DEVICES EXTRACTION')
|
||||
|
||||
#================================
|
||||
# ----- MOSFET EXTRACTION -------
|
||||
#================================
|
||||
|
||||
cheat(SRAM_SHAREDSD_CELLS) {
|
||||
|
||||
# %include rule_decks/mos_extraction.lvs
|
||||
|
||||
}
|
||||
|
||||
#================================
|
||||
# ---- RF-MOSFET EXTRACTION -----
|
||||
#================================
|
||||
|
||||
# %include rule_decks/rfmos_extraction.lvs
|
||||
|
||||
#================================
|
||||
# ------- BJT EXTRACTION --------
|
||||
#================================
|
||||
|
||||
# %include rule_decks/bjt_extraction.lvs
|
||||
|
||||
#================================
|
||||
# ------ DIODE EXTRACTION -------
|
||||
#================================
|
||||
|
||||
# %include rule_decks/diode_extraction.lvs
|
||||
|
||||
#================================
|
||||
# ---- RESISTOR EXTRACTIONS -----
|
||||
#================================
|
||||
|
||||
# %include rule_decks/res_extraction.lvs
|
||||
|
||||
#==================================
|
||||
# --------- CAP EXTRACTION --------
|
||||
#==================================
|
||||
|
||||
# %include rule_decks/cap_extraction.lvs
|
||||
|
||||
#================================
|
||||
# ------- ESD EXTRACTION --------
|
||||
#================================
|
||||
|
||||
# %include rule_decks/esd_extraction.lvs
|
||||
|
||||
#=================================
|
||||
# ----- Inductor EXTRACTIONS -----
|
||||
#=================================
|
||||
|
||||
# %include rule_decks/ind_extraction.lvs
|
||||
|
||||
#================================
|
||||
# ------- Taps EXTRACTIONS ------
|
||||
#================================
|
||||
|
||||
# %include rule_decks/tap_extraction.lvs
|
||||
end
|
||||
|
||||
# RF MOS model mapping
|
||||
logger.info('Starting SG13G2 LVS RF MOS Model Mapping')
|
||||
# %include rule_decks/rfmos_model_mapping.lvs
|
||||
|
||||
#==================================================
|
||||
# ------------ COMPARISON PREPARATIONS ------------
|
||||
#==================================================
|
||||
|
||||
logger.info('Starting SG13G2 LVS Options Preparations')
|
||||
|
||||
log_option_action = lambda do |label, option_name, enabled|
|
||||
state = enabled ? 'ENABLED' : 'SKIPPED'
|
||||
logger.info("[#{label}] #{option_name}: #{state}")
|
||||
end
|
||||
|
||||
apply_netlist_options = lambda do |target_netlist, label|
|
||||
log_option_action.call(label, 'simplify', SIMPLIFY)
|
||||
target_netlist.simplify if SIMPLIFY
|
||||
|
||||
log_option_action.call(label, 'make_top_level_pins', TOP_LVL_PINS)
|
||||
target_netlist.make_top_level_pins if TOP_LVL_PINS
|
||||
|
||||
log_option_action.call(label, 'combine_devices', COMBINE_DEVICES)
|
||||
target_netlist.combine_devices if COMBINE_DEVICES
|
||||
|
||||
log_option_action.call(label, 'purge', PURGE)
|
||||
target_netlist.purge if PURGE
|
||||
|
||||
log_option_action.call(label, 'purge_nets', PURGE_NETS)
|
||||
target_netlist.purge_nets if PURGE_NETS
|
||||
end
|
||||
|
||||
if NET_ONLY
|
||||
logger.info('NET_ONLY enabled: apply extraction netlist options and skip comparison.')
|
||||
if LAYOUT_NETLIST
|
||||
logger.info('SG13G2 LVS flow: layout_netlist input -> option preparation -> write netlist -> exit.')
|
||||
else
|
||||
logger.info('SG13G2 LVS flow: extraction -> option preparation -> write netlist -> exit.')
|
||||
end
|
||||
logger.info('Starting SG13G2 LVS Simplification')
|
||||
apply_netlist_options.call(netlist, 'layout_netlist')
|
||||
netlist
|
||||
else
|
||||
if LAYOUT_NETLIST
|
||||
logger.info('SG13G2 LVS flow: layout_netlist input -> alignment -> option preparation -> comparison.')
|
||||
else
|
||||
logger.info('SG13G2 LVS flow: extraction -> alignment -> option preparation -> comparison.')
|
||||
end
|
||||
# === Aligns the extracted netlist vs. the schematic ===
|
||||
logger.info('Starting SG13G2 LVS Alignment')
|
||||
align
|
||||
|
||||
#=== NETLIST OPTIONS ===
|
||||
logger.info('Starting SG13G2 LVS Simplification')
|
||||
apply_netlist_options.call(netlist, 'layout_netlist')
|
||||
apply_netlist_options.call(schematic, 'schematic_netlist')
|
||||
|
||||
#=== IGNORE EXTREME VALUES ===
|
||||
max_res(1e9)
|
||||
min_caps(1e-18)
|
||||
|
||||
# === COMPARISON ===
|
||||
if IGNORE_TOP_PORTS_MISMATCH
|
||||
logger.info('Starting SG13G2 LVS Comparison')
|
||||
success = compare
|
||||
else
|
||||
logger.info('Starting SG13G2 LVS Comparison in strict port mode.')
|
||||
logger.info('flag_missing_ports enabled: missing/mislabeled top-level ports are treated as errors.')
|
||||
success = compare && flag_missing_ports
|
||||
end
|
||||
|
||||
#================================================
|
||||
#------------- COMPARISON RESULTS ---------------
|
||||
#================================================
|
||||
|
||||
if ! success
|
||||
logger.info('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
|
||||
logger.error("ERROR : Netlists don't match")
|
||||
logger.info('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
|
||||
else
|
||||
logger.info('==========================================')
|
||||
logger.info('INFO : Congratulations! Netlists match.')
|
||||
logger.info('==========================================')
|
||||
end
|
||||
end
|
||||
|
||||
exec_end_time = Time.now
|
||||
run_time = exec_end_time - exec_start_time
|
||||
logger.info(format('LVS Total Run time %f seconds', run_time))
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
#!/usr/bin/env python3
|
||||
# See LICENSE for licensing information.
|
||||
#
|
||||
"""IHP SG13G2 technology definition."""
|
||||
|
||||
import os
|
||||
from openram import drc as d
|
||||
|
||||
tech_modules = d.module_type()
|
||||
|
||||
cell_properties = d.cell_properties()
|
||||
cell_properties.ptx.model_is_subckt = True
|
||||
|
||||
###################################################
|
||||
# GDS file info
|
||||
###################################################
|
||||
GDS = {}
|
||||
GDS["unit"] = (0.001, 1e-6)
|
||||
GDS["zoom"] = 0.5
|
||||
|
||||
###################################################
|
||||
# Interconnect stacks
|
||||
###################################################
|
||||
poly_stack = ("poly", "contact", "m1")
|
||||
active_stack = ("active", "contact", "m1")
|
||||
m1_stack = ("m1", "via1", "m2")
|
||||
m2_stack = ("m2", "via2", "m3")
|
||||
m3_stack = ("m3", "via3", "m4")
|
||||
m4_stack = ("m4", "via4", "m5")
|
||||
m5_stack = ("m5", "topvia1", "topmetal1")
|
||||
topmetal1_stack = ("topmetal1", "topvia2", "topmetal2")
|
||||
|
||||
lef_rom_interconnect = ["m1", "m2", "m3", "m4", "m5", "topmetal1", "topmetal2"]
|
||||
|
||||
layer_indices = {"poly": 0,
|
||||
"active": 0,
|
||||
"nwell": 0,
|
||||
"pwell": 0,
|
||||
"m1": 1,
|
||||
"m2": 2,
|
||||
"m3": 3,
|
||||
"m4": 4,
|
||||
"m5": 5,
|
||||
"topmetal1": 6,
|
||||
"topmetal2": 7}
|
||||
|
||||
feol_stacks = [poly_stack, active_stack]
|
||||
beol_stacks = [m1_stack, m2_stack, m3_stack, m4_stack, m5_stack, topmetal1_stack]
|
||||
layer_stacks = feol_stacks + beol_stacks
|
||||
|
||||
preferred_directions = {"poly": "V",
|
||||
"active": "V",
|
||||
"m1": "H",
|
||||
"m2": "V",
|
||||
"m3": "H",
|
||||
"m4": "V",
|
||||
"m5": "H",
|
||||
"topmetal1": "V",
|
||||
"topmetal2": "H"}
|
||||
|
||||
power_grid = topmetal1_stack
|
||||
|
||||
###################################################
|
||||
# GDS Layer Map
|
||||
###################################################
|
||||
layer = {}
|
||||
layer["activ"] = (1, 0)
|
||||
layer["active"] = layer["activ"]
|
||||
layer["gatpoly"] = (5, 0)
|
||||
layer["poly"] = layer["gatpoly"]
|
||||
layer["cont"] = (6, 0)
|
||||
layer["contact"] = layer["cont"]
|
||||
layer["metal1"] = (8, 0)
|
||||
layer["m1"] = layer["metal1"]
|
||||
layer["via1"] = (19, 0)
|
||||
layer["metal2"] = (10, 0)
|
||||
layer["m2"] = layer["metal2"]
|
||||
layer["via2"] = (29, 0)
|
||||
layer["metal3"] = (30, 0)
|
||||
layer["m3"] = layer["metal3"]
|
||||
layer["via3"] = (49, 0)
|
||||
layer["metal4"] = (50, 0)
|
||||
layer["m4"] = layer["metal4"]
|
||||
layer["via4"] = (66, 0)
|
||||
layer["metal5"] = (67, 0)
|
||||
layer["m5"] = layer["metal5"]
|
||||
layer["topvia1"] = (125, 0)
|
||||
layer["topmetal1"] = (126, 0)
|
||||
layer["topvia2"] = (133, 0)
|
||||
layer["topmetal2"] = (134, 0)
|
||||
layer["nwell"] = (31, 0)
|
||||
layer["pwell"] = (46, 0)
|
||||
layer["text"] = (63, 0)
|
||||
layer["boundary"] = (189, 4)
|
||||
layer["mem"] = (189, 4)
|
||||
|
||||
label_purpose = 25
|
||||
|
||||
layer_names = {}
|
||||
for openram_name, pdk_name in [("active", "Activ"),
|
||||
("poly", "GatPoly"),
|
||||
("contact", "Cont"),
|
||||
("m1", "Metal1"),
|
||||
("via1", "Via1"),
|
||||
("m2", "Metal2"),
|
||||
("via2", "Via2"),
|
||||
("m3", "Metal3"),
|
||||
("via3", "Via3"),
|
||||
("m4", "Metal4"),
|
||||
("via4", "Via4"),
|
||||
("m5", "Metal5"),
|
||||
("topvia1", "TopVia1"),
|
||||
("topmetal1", "TopMetal1"),
|
||||
("topvia2", "TopVia2"),
|
||||
("topmetal2", "TopMetal2"),
|
||||
("nwell", "NWell"),
|
||||
("pwell", "PWell"),
|
||||
("text", "TEXT"),
|
||||
("boundary", "prBoundary"),
|
||||
("mem", "prBoundary")]:
|
||||
layer_names[openram_name] = pdk_name
|
||||
|
||||
###################################################
|
||||
# DRC/LVS rules setup
|
||||
###################################################
|
||||
parameter = {}
|
||||
parameter["min_tx_size"] = 0.13
|
||||
parameter["beta"] = 2
|
||||
parameter["6T_inv_nmos_size"] = 0.26
|
||||
parameter["6T_inv_pmos_size"] = 0.26
|
||||
parameter["6T_access_size"] = 0.26
|
||||
parameter["le_tau"] = 2.25
|
||||
parameter["cap_relative_per_ff"] = 7.5
|
||||
parameter["dff_clk_cin"] = 30.6
|
||||
parameter["6tcell_wl_cin"] = 3
|
||||
parameter["min_inv_para_delay"] = 2.4
|
||||
parameter["sa_en_pmos_size"] = 0.72
|
||||
parameter["sa_en_nmos_size"] = 0.27
|
||||
parameter["sa_inv_pmos_size"] = 0.54
|
||||
parameter["sa_inv_nmos_size"] = 0.27
|
||||
parameter["bitcell_drain_cap"] = 0.1
|
||||
|
||||
drc = d.design_rules("ihp_sg13g2")
|
||||
drc["grid"] = 0.005
|
||||
drc["minwidth_tx"] = 0.13
|
||||
drc["minlength_channel"] = 0.13
|
||||
drc["pwell_to_nwell"] = 1.8
|
||||
|
||||
drc.add_layer("active", width=0.15, spacing=0.21)
|
||||
drc.add_layer("poly", width=0.13, spacing=0.18)
|
||||
drc.add_layer("contact", width=0.16, spacing=0.18)
|
||||
drc.add_layer("m1", width=0.16, spacing=0.18)
|
||||
drc.add_layer("m2", width=0.20, spacing=0.21)
|
||||
drc.add_layer("m3", width=0.20, spacing=0.21)
|
||||
drc.add_layer("m4", width=0.20, spacing=0.21)
|
||||
drc.add_layer("m5", width=0.20, spacing=0.21)
|
||||
drc.add_layer("topmetal1", width=1.64, spacing=1.64)
|
||||
drc.add_layer("topmetal2", width=2.0, spacing=2.0)
|
||||
|
||||
drc["minwidth_contact"] = 0.16
|
||||
drc["contact_to_contact"] = 0.18
|
||||
drc["minwidth_via1"] = 0.19
|
||||
drc["via1_to_via1"] = 0.22
|
||||
drc["minwidth_via2"] = 0.19
|
||||
drc["via2_to_via2"] = 0.22
|
||||
drc["minwidth_via3"] = 0.19
|
||||
drc["via3_to_via3"] = 0.22
|
||||
drc["minwidth_via4"] = 0.19
|
||||
drc["via4_to_via4"] = 0.22
|
||||
|
||||
###################################################
|
||||
# Spice parameters
|
||||
###################################################
|
||||
spice = {}
|
||||
spice["power"] = "VDD"
|
||||
spice["ground"] = "VSS"
|
||||
spice["nmos"] = "sg13_lv_nmos"
|
||||
spice["pmos"] = "sg13_lv_pmos"
|
||||
spice["fet_models"] = {"TT": "mos_tt", "SS": "mos_ss", "FF": "mos_ff"}
|
||||
spice["fet_libraries"] = {
|
||||
"TT": [[os.path.join(os.environ.get("SPICE_MODEL_DIR", ""), "cornerMOSlv.lib"), "mos_tt"]],
|
||||
"SS": [[os.path.join(os.environ.get("SPICE_MODEL_DIR", ""), "cornerMOSlv.lib"), "mos_ss"]],
|
||||
"FF": [[os.path.join(os.environ.get("SPICE_MODEL_DIR", ""), "cornerMOSlv.lib"), "mos_ff"]],
|
||||
}
|
||||
spice["supply_voltages"] = [1.08, 1.20, 1.32]
|
||||
spice["temperatures"] = [125, 25, -55]
|
||||
spice["nom_supply_voltage"] = 1.20
|
||||
spice["nom_temperature"] = 25
|
||||
spice["dff_in_cap"] = 0.001
|
||||
spice["rise_time"] = 0.05
|
||||
spice["fall_time"] = 0.05
|
||||
spice["feasible_period"] = 10
|
||||
spice["c_g_ideal"] = 0
|
||||
spice["c_overlap"] = 0
|
||||
spice["c_fringe"] = 0
|
||||
spice["wire_unit_r"] = 0
|
||||
spice["wire_unit_c"] = 0
|
||||
spice["wire_r_per_um"] = 0
|
||||
spice["wire_c_per_um"] = 0
|
||||
spice["min_tx_gate_c"] = 0.1
|
||||
spice["min_tx_drain_c"] = 0.1
|
||||
spice["dff_out_cap"] = 0.1
|
||||
spice["nom_threshold"] = 0.4
|
||||
spice["default_event_frequency"] = 100
|
||||
spice["inv_leakage"] = 0
|
||||
spice["nand2_leakage"] = 0
|
||||
spice["nand3_leakage"] = 0
|
||||
spice["nand4_leakage"] = 0
|
||||
spice["nor2_leakage"] = 0
|
||||
spice["dff_leakage"] = 0
|
||||
spice["bitcell_leakage"] = 0
|
||||
spice["sa_transconductance"] = 1
|
||||
spice["dff_setup"] = 0
|
||||
spice["dff_hold"] = 0
|
||||
|
||||
drc_name = "klayout"
|
||||
lvs_name = "klayout"
|
||||
pex_name = "magic"
|
||||
|
||||
layer_properties = d.layer_properties()
|
||||
Loading…
Reference in New Issue