blob: 55b447bd8b80d6fcf5ed1cd73fdf2c2e751da38c [file] [log] [blame]
# Copyright 2022 GlobalFoundries 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
#
# http://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.
"""Run GlobalFoundries 180nm MCU LVS Regression.
Usage:
run_regression.py (--help| -h)
run_regression.py (--run_dir=<out_dir>) [--num_cores=<num>] [--device=<device_name>]
Options:
--help -h Print this help message.
--run_dir=<out_dir> Selecting your output path.
--num_cores=<num> Number of cores to be used by LVS checker
--device=<device_name> Selecting device option. Allowed values (MOS, BJT, DIODE, RES, MIMCAP, MOSCAP, MOS-SAB). [default: ALL]
"""
from docopt import docopt
import os
import time
import datetime
import logging
def lvs_check(table,files):
## TODO : Add run folder to save all the runs inside.
# # set folder structure for each run
# x = f"{datetime.datetime.now()}"
# x = x.replace(" ", "_")
# name_ext = str(rule_deck_path).replace(".drc","").split("/")[-1]
# os.system(f"mkdir run_{x}_{name_ext}")
pass_count = 0
fail_count = 0
logging.info(f"================================================")
logging.info('{:-^48}'.format(table.upper()))
logging.info(f"================================================ \n")
# Get manual test cases
x = os.popen("find man_testing/ -name *.gds").read()
man_testing = x.split("\n")[:-1]
# Generate databases
for file in files:
layout = file[0]
# Get switches
if layout == "sample_ggnmos_6p0_sab":
switches = ' -rd lvs_sub=sub!'
else:
switches = ' -rd lvs_sub=vdd!'
if len(file) > 1:
switches = file[1] + switches
# Check if file is mosfet or esd
if "sample" in layout:
net = f"{layout}.src"
else:
net = layout
if os.path.exists(f'testcases/{net}.cdl'):
# Get netlist with $ and ][
with open(f'testcases/{net}.cdl', 'r') as file :
spice_netlist = file.read()
# Replace the target string
spice_netlist = spice_netlist.replace('$SUB=', '')
spice_netlist = spice_netlist.replace('$', '')
spice_netlist = spice_netlist.replace('[', '')
spice_netlist = spice_netlist.replace(']', '')
# Write the file out again
with open(f'testcases/{layout}_generated.cdl', 'w') as file:
file.write(spice_netlist)
result = os.popen(f"klayout -b -r ../gf180mcu.lvs -rd input=testcases/{layout}.gds -rd report={layout}.lvsdb -rd schematic={layout}_generated.cdl -rd target_netlist={layout}_extracted.cir -rd thr={workers_count} {switches}").read()
# moving all reports to run dir
out_dir = arguments["--run_dir"]
device_dir = table.split(" ")[0]
os.system(f"mv -f testcases/{layout}.lvsdb testcases/{layout}_extracted.cir testcases/{layout}_generated.cdl {out_dir}/LVS_{device_dir}/")
if "INFO : Congratulations! Netlists match." in result:
logging.info(f"Extraction of {layout} is passed")
pass_count = pass_count + 1
else:
pass_before = pass_count
fail_before = fail_count
logging.error(f"Extraction of {layout} in fab provided test case is failed.")
for file in man_testing:
file_clean = file.split("/")[-1].replace(".gds","")
if layout == file_clean:
result = os.popen(f"klayout -b -r ../gf180mcu.lvs -rd input={file} -rd report={layout}.lvsdb -rd schematic={layout}.cdl -rd target_netlist={layout}_extracted.cir -rd thr={workers_count} {switches}").read()
dir_clean = file.replace(".gds","")
os.system(f"mv -f {dir_clean}.lvsdb {dir_clean}_extracted.cir {out_dir}/LVS_{device_dir}/")
if "INFO : Congratulations! Netlists match." in result:
logging.info(f"Extraction of {layout} in manual test case is passed")
pass_count = pass_count + 1
break
else:
logging.error(f"Extraction of {layout} in manual test case is failed")
fail_count = fail_count + 1
break
if (pass_before == pass_count) and (fail_before == fail_count):
logging.error(f"{layout} is not in manual test case, Then extraction of {layout} is failed")
fail_count = fail_count + 1
logging.info("\n==================================")
logging.info(f"NO. OF PASSED {table} : {pass_count}")
logging.info(f"NO. OF FAILED {table} : {fail_count}")
logging.info("==================================\n")
def main():
# logs format
logging.basicConfig(level=logging.DEBUG, format=f"%(asctime)s | %(levelname)-7s | %(message)s", datefmt='%d-%b-%Y %H:%M:%S')
# Check out_dir existance
out_dir = arguments["--run_dir"]
if os.path.exists(out_dir) and os.path.isdir(out_dir):
pass
else:
logging.error("This run directory doesn't exist. Please recheck.")
exit ()
# MOSFET
mosfet_files = [['sample_pmos_6p0_dw'], ['sample_nmos_10p0_asym'], ['sample_nmos_3p3'], ['sample_pmos_5p0_dw'], ['sample_pmos_6p0'], ['sample_nmos_5p0'], ['sample_nmos_6p0'], ['sample_nmos_6p0_dw'], ['sample_pmos_10p0_asym'], ['sample_nmos_5p0_dw'], ['sample_pmos_5p0'], ['sample_pmos_3p3'], ['sample_nmos_6p0_nat']]
# BJT
bjt_files = [['vnpn_10x10'], ['vnpn_5x5'], ['vnpn_0p54x16'], ['vnpn_0p54x8'], ['vnpn_0p54x4'], ['vnpn_0p54x2'], ['vpnp_10x10'], ['vpnp_5x5'], ['vpnp_0p42x10'], ['vpnp_0p42x5']]
# Diode
diode_files = [['np_3p3'], ['np_3p3_dw'], ['np_6p0'], ['np_6p0_dw'], ['pn_3p3'], ['pn_3p3_dw'], ['pn_6p0'], ['pn_6p0_dw'], ['nwp_3p3'], ['nwp_6p0'], ['dnwpw_3p3'], ['dnwpw_6p0'], ['dnwps_3p3'], ['dnwps_6p0'], ['sc_diode']]
# Resistor
resistor_files = [['pplus_u'], ['nplus_s'], ['pplus_u_dw'], ['nplus_s_dw'], ['pplus_s'], ['pplus_s_dw'], ['nplus_u_dw'], ['nplus_u'], ['nwell'], ['pwell'], ['ppolyf_s'], ['ppolyf_u_3k', '-rd poly_res=3k'], ['ppolyf_s_dw'], ['ppolyf_u_1k', '-rd poly_res=1k'], ['ppolyf_u_3k_6p0_dw', '-rd poly_res=3k'], ['npolyf_u_dw'], ['ppolyf_u_3k_dw', '-rd poly_res=3k'], ['ppolyf_u_3k_6p0', '-rd poly_res=3k'], ['npolyf_s'], ['ppolyf_u_1k_6p0_dw', '-rd poly_res=1k'], ['ppolyf_u'], ['npolyf_u'], ['ppolyf_u_2k_dw', '-rd poly_res=2k'], ['npolyf_s_dw'], ['ppolyf_u_2k_6p0_dw', '-rd poly_res=2k'], ['ppolyf_u_2k_6p0', '-rd poly_res=2k'], ['ppolyf_u_dw'], ['ppolyf_u_1k_6p0', '-rd poly_res=1k'], ['ppolyf_u_2k', '-rd poly_res=2k'], ['ppolyf_u_1k_dw', '-rd poly_res=1k'], ['rm1'], ['rm2'], ['rm3'], ['tm6k', '-rd metal_top=6K'], ['tm9k', '-rd metal_top=9K'], ['tm30k', '-rd metal_top=30K'], ['tm11k', '-rd metal_top=11K']]
# MIM Capacitor
mim_files = [['mim_1p0fF', '-rd mim_option=A -rd mim_cap=1'], ['mim_1p5fF', '-rd mim_option=A -rd mim_cap=1.5'], ['mim_2p0fF', '-rd mim_option=A -rd mim_cap=2'],['mim_1p0fF_tm', '-rd mim_option=B -rd mim_cap=1'], ['mim_1p5fF_tm', '-rd mim_option=B -rd mim_cap=1.5'], ['mim_2p0fF_tm', '-rd mim_option=B -rd mim_cap=2']]
# MOS Capacitor
moscap_files = [['pmoscap_3p3_b'], ['nmoscap_3p3_b'], ['nmoscap_3p3'], ['nmoscap_6p0_b'], ['pmoscap_6p0_dw'], ['nmoscap_6p0'], ['pmoscap_3p3_dw'], ['pmoscap_6p0'], ['nmoscap_3p3_dw'], ['pmoscap_6p0_b'], ['pmoscap_3p3'], ['nmoscap_6p0_dw']]
# ESD (SAB MOSFET)
esd_files = [['sample_pmos_5p0_sab'], ['sample_nmos_5p0_sab'], ['sample_pmos_3p3_dw_sab'], ['sample_pmos_3p3_sab'], ['sample_pmos_5p0_dw_sab'], ['sample_nmos_6p0_sab'], ['sample_pmos_6p0_dw_sab'], ['sample_nmos_6p0_dw_sab'], ['sample_nmos_3p3_dw_sab'], ['sample_nmos_5p0_dw_sab'], ['sample_nmos_3p3_sab'], ['sample_pmos_6p0_sab'],
['sample_ggnmos_3p3_sab'], ['sample_ggnmos_6p0_dw_sab'], ['sample_ggnmos_6p0_sab'], ['sample_ggnmos_5p0_sab'], ['sample_ggnmos_5p0_dw_sab'], ['sample_ggnmos_3p3_dw_sab'], ['sample_gppmos_3p3_dw_sab'], ['sample_gppmos_6p0_sab'], ['sample_gppmos_5p0_dw_sab'], ['sample_gppmos_3p3_sab'], ['sample_gppmos_6p0_dw_sab'], ['sample_gppmos_5p0_sab']
]
# eFuse
efuse_files = [['efuse']]
logging.info("\n================================")
logging.info("Running LVS regression")
logging.info("================================\n")
if arguments["--device"] == "MOS":
lvs_check ("MOS DEVICES" , mosfet_files)
elif arguments["--device"] == "BJT":
lvs_check ("BJT DEVICES" , bjt_files)
elif arguments["--device"] == "DIODE":
lvs_check ("DIODE DEVICES" , diode_files)
elif arguments["--device"] == "RES":
lvs_check ("RES DEVICES" , resistor_files)
elif arguments["--device"] == "MIMCAP":
lvs_check ("MIMCAP DEVICES" , mim_files)
elif arguments["--device"] == "MOSCAP":
lvs_check ("MOSCAP DEVICES" , moscap_files)
elif arguments["--device"] == "MOS-SAB":
lvs_check ("MOS-SAB DEVICES" , esd_files)
elif arguments["--device"] == "EFUSE":
lvs_check ("EFUSE DEVICES" , efuse_files)
else:
lvs_check ("MOS DEVICES" , mosfet_files)
lvs_check ("BJT DEVICES" , bjt_files)
lvs_check ("DIODE DEVICES" , diode_files)
lvs_check ("RES DEVICES" , resistor_files)
lvs_check ("MIM DEVICES" , mim_files)
lvs_check ("MOSCAP DEVICES" , moscap_files)
lvs_check ("ESD DEVICES" , esd_files)
lvs_check ("EFUSE DEVICES" , efuse_files)
if __name__ == "__main__":
# Args
arguments = docopt(__doc__, version='LVS REGRESSION: 0.1')
workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
# Calling main function
main()