diff --git a/models/xyce/smbb000149.xyce b/models/xyce/smbb000149.xyce index 87895c74..b4778693 100644 --- a/models/xyce/smbb000149.xyce +++ b/models/xyce/smbb000149.xyce @@ -47,34 +47,12 @@ + pfet_10v0_asym_DCGS=1 pfet_10v0_asym_DCGD=1 pfet_10v0_asym_DCJS=1 pfet_10v0_asym_DCJD=1 *************10V************ .LIB smbb000149.xyce nfet_10v0_asym_t - - - - - - - - - - - - - - - - - - - - - .LIB smbb000149.xyce pfet_10v0_asym_t .LIB smbb000149.xyce noise_corner -.ENDL +.ENDL typical .LIB ss - .PARAM + nfet_10v0_asym_DTOX=8E-10 nfet_10v0_asym_DXL=6e-008 nfet_10v0_asym_DXW=-3.46E-8 nfet_10v0_asym_DVTH0=0.112 + nfet_10v0_asym_DRDSW=1.2 nfet_10v0_asym_DRDRIFT=1.271 nfet_10v0_asym_DVSAT=0.926 @@ -84,34 +62,12 @@ + pfet_10v0_asym_DRDRIFT=1.144 pfet_10v0_asym_DVSAT=0.91 pfet_10v0_asym_DU0=0.964 + pfet_10v0_asym_DCGS=1.1 pfet_10v0_asym_DCGD=1.2 pfet_10v0_asym_DCJS=1.1 pfet_10v0_asym_DCJD=1.2 .LIB smbb000149.xyce nfet_10v0_asym_t - - - - - - - - - - - - - - - - - - - - - .LIB smbb000149.xyce pfet_10v0_asym_t .LIB smbb000149.xyce noise_corner -.ENDL +.ENDL ss .LIB ff - .PARAM + nfet_10v0_asym_DTOX=-8E-10 nfet_10v0_asym_DXL=-6e-008 nfet_10v0_asym_DXW=3.46E-8 + nfet_10v0_asym_DVTH0=-0.10388 nfet_10v0_asym_DRDSW=0.868 nfet_10v0_asym_DRDRIFT=0.8245 @@ -121,35 +77,12 @@ + pfet_10v0_asym_DRDRIFT=0.89 pfet_10v0_asym_DVSAT=1.06 pfet_10v0_asym_DU0=1.03 pfet_10v0_asym_DCGS=0.9 + pfet_10v0_asym_DCGD=0.8 pfet_10v0_asym_DCJS=0.9 pfet_10v0_asym_DCJD=0.8 .LIB smbb000149.xyce nfet_10v0_asym_t - - - - - - - - - - - - - - - - - - - - - - .LIB smbb000149.xyce pfet_10v0_asym_t .LIB smbb000149.xyce noise_corner -.ENDL - -.LIB sf +.ENDL ff +.LIB sf .PARAM + nfet_10v0_asym_DTOX=0 nfet_10v0_asym_DXL=5.024e-008 nfet_10v0_asym_DXW=0 nfet_10v0_asym_DVTH0=0.068 + nfet_10v0_asym_DRDSW=1.2 nfet_10v0_asym_DRDRIFT=1.156 nfet_10v0_asym_DVSAT=0.928 @@ -159,34 +92,12 @@ + pfet_10v0_asym_DVSAT=1.012 pfet_10v0_asym_DU0=1.03 pfet_10v0_asym_DCGS=0.93 pfet_10v0_asym_DCGD=0.86 + pfet_10v0_asym_DCJS=0.93 pfet_10v0_asym_DCJD=0.86 .LIB smbb000149.xyce nfet_10v0_asym_t - - - - - - - - - - - - - - - - - - - - - - .LIB smbb000149.xyce pfet_10v0_asym_t .LIB smbb000149.xyce noise_corner -.ENDL +.ENDL sf -.LIB fs +.LIB fs .PARAM + nfet_10v0_asym_DTOX=0 nfet_10v0_asym_DXL=-5.02e-008 nfet_10v0_asym_DXW=0 nfet_10v0_asym_DVTH0=-0.058169 + nfet_10v0_asym_DRDSW=0.868 nfet_10v0_asym_DRDRIFT=0.89748 nfet_10v0_asym_DVSAT=1.045 @@ -196,33 +107,12 @@ + pfet_10v0_asym_DVSAT=0.989 pfet_10v0_asym_DU0=0.97 pfet_10v0_asym_DCGS=1.07 pfet_10v0_asym_DCGD=1.14 + pfet_10v0_asym_DCJS=1.07 pfet_10v0_asym_DCJD=1.14 .LIB smbb000149.xyce nfet_10v0_asym_t - - - - - - - - - - - - - - - - - - - - .LIB smbb000149.xyce pfet_10v0_asym_t .LIB smbb000149.xyce noise_corner -.ENDL +.ENDL fs .LIB statistical - .PARAM + MC_VSAT2_10V={agauss(0, 1, 3)} MC_RD_10V_2={agauss(0, 1, 3)} MC_U0_10V_2={agauss(0, 1, 3)} + MC_CGOL_10V_2={agauss(0, 1, 3)} MC_VSATN2_10V={agauss(0, 1, 3)} MC_RDN_10V_2={agauss(0, 1, 3)} @@ -244,22 +134,6 @@ + nfet_10v0_asym_DCGD='(1+(24e-3* mc_cgol_10V+24e-3* mc_cgolN_10V)*sw_stat_global*mc_skew)' + nfet_10v0_asym_DCJS='(1+(12e-3* mc_cgol_10V+12e-3* mc_cgolN_10V)*sw_stat_global*mc_skew)' + nfet_10v0_asym_DCJD='(1+(24e-3* mc_cgol_10V+24e-3* mc_cgolN_10V)*sw_stat_global*mc_skew)' - - - - - - - - - - - - - - - - .PARAM + pfet_10v0_asym_SIG_DVTH1='0.01692*(-0.7*mc_sig_vth+0.7*mc_sig_vthp)*sw_stat_global*mc_skew' + pfet_10v0_asym_DTOX='7.143e-11*(0.77*mc_toxe+0.63*mc_toxep)*sw_stat_global*mc_skew' @@ -273,28 +147,10 @@ + pfet_10v0_asym_DCJS='(1+(12e-3*mc_cgol_10V + 12e-3*mc_cgolp_10V)*sw_stat_global*mc_skew)' + pfet_10v0_asym_DCJD='(1+(24e-3*mc_cgol_10V + 24e-3*mc_cgolp_10V)*sw_stat_global*mc_skew)' - - - .LIB smbb000149.xyce nfet_10v0_asym_t - - - - - - - - - - - - - - - .LIB smbb000149.xyce pfet_10v0_asym_t .LIB smbb000149.xyce noise_corner -.ENDL +.ENDL statistical * .LIB noise_corner @@ -302,11 +158,7 @@ + nfet_10v0_asym_NOIA='(fnoicor==0)*1.1021E42 + (fnoicor==1)*2.5852e42' nfet_10v0_asym_NOIB='(fnoicor==0)*2.8476E24 + (fnoicor==1)*6.5096e+024' + nfet_10v0_asym_NOIC='(fnoicor==0)*8.75 + (fnoicor==1)*8.75' pfet_10v0_asym_NOIA='(fnoicor==0)*2.9073e+041 + (fnoicor==1)*1.7073e+042' + pfet_10v0_asym_NOIB='(fnoicor==0)*8.0736e+025 + (fnoicor==1)*2.4523e+026' pfet_10v0_asym_NOIC='(fnoicor==0)*12780 + (fnoicor==1)*12780' -.ENDL - - - - +.ENDL noise_corner * * @@ -329,13 +181,6 @@ + CGS_FACTOR2=1 CGDV_D2=-2.1473 CGDL_D=1.3222E-11 CGS_VTH1=0.20635 .PARAM CGDS_FIXED='3.9*8.854e-12/toxep' - - - - - - - Rdrift d d2 + TC1={trx1} TC2={trx2} M={nf} + R='(rdrift1*nfet_10v0_asym_drdrift)*1.2e-6/(w/nf-wa)' @@ -395,48 +240,8 @@ C3_gb g b + WW=0 WWL=0 WWN=1 XJ=1.5E-7 XJBVD=1 XJBVS=1 XL='0+nfet_10v0_asym_dxl' XPART=1 + XTID=3 XTIS=3 XTSD=0.63818 XW='0+nfet_10v0_asym_dxw' + LEVEL=14 -***** Flag Parameter *** -***** Geometry Range Parameter *** -***** Process Parameter *** -***** dW and dL Parameter *** -***** Vth Related Parameter *** -***** Mobility Related Parameter *** -***** Subthreshold Related Parameter *** -***** Output Resistance Related Parameter *** -***** GIDL Effect Parameters *** -***** Flicker Noise Model Parameter *** -***** Capacitance Parameter *** - -***** Souce/Drain Junction Diode Model Parameter *** - - - - - -***** Temperature coefficient *** - - - - - - - - - - - .ENDS nfet_10v0_asym - - - - - - - - - - .ENDL nfet_10v0_asym_t * @@ -460,12 +265,6 @@ C3_gb g b + CGS_FACTOR2=1 CGDV_D2=-1.2197 CGDL_D=1.6046E-11 CGS_VTH1=0.13041 .PARAM CGDS_FIXED='3.9*8.854e-12/toxep' - - - - - - Rd1 d d2 + TC1={trx1} TC2={trx2} M={nf} R='(rdrift*pfet_10v0_asym_drdrift)/(w/nf-wa)' Rd2 d2 d1 @@ -525,59 +324,10 @@ C3_gb g b + WUTE=-4E-8 WVSAT=0 WVTH0=0 WW=0 WWL=0 XJ=1.5E-7 XJBVD=1 XJBVS=1 + XL=' 0 + pfet_10v0_asym_dxl' XTSD=0.92538 XW='0+ pfet_10v0_asym_dxw' + LEVEL=14 -***** Flag Parameter *** -***** Geometry Range Parameter *** -***** Process Parameter *** -***** dW and dL Parameter *** -***** Vth Related Parameter *** -***** Mobility Related Parameter *** -***** Subthreshold Related Parameter *** -***** Output Resistance Related Parameter *** -***** Gate Dielectric Tunneling Current *** -***** GIDL Effect Parameters *** - - - - - - -***** Flicker Noise Model Parameter *** - - -***** Capacitance Parameter *** - - -***** Souce/Drain Junction Diode Model Parameter *** - - - - - - -***** Temperature coefficient *** - - - - - - - - - - - .ENDS pfet_10v0_asym - - - - - .ENDL pfet_10v0_asym_t - - ************************end of file************************* - * diff --git a/models/xyce/testing/regression/mos_hv_id/device_netlists_id/nfet.spice b/models/xyce/testing/regression/mos_hv_id/device_netlists_id/nfet.spice new file mode 100644 index 00000000..c5e4e9b4 --- /dev/null +++ b/models/xyce/testing/regression/mos_hv_id/device_netlists_id/nfet.spice @@ -0,0 +1,38 @@ +*************************** +** nfet_03v3_t_id +*************************** +* Copyright 2023 Efabless Corporation +* +* 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. + +vds D_tn 0 dc {{vds_val}} +vgs G_tn 0 dc 10 +Vbs B_tn 0 dc {{vbs_val}} + +.STEP TEMP {{temp}} {{temp}} {{temp}} + +xmn1 D_tn G_tn 0 B_tn {{device}} W = {{width}}u L = {{length}}u + +**** begin architecture code +.DC {{sweeps}} + +.print DC FORMAT=CSV file={{result_path}} v(D_tn) v(G_tn) v(B_tn) {-I(Vds)} + +** library calling + +.include {{model_design_path}} +.lib {{model_card_path}} {{corner}} + +**** end architecture code + +.end diff --git a/models/xyce/testing/regression/mos_hv_id/device_netlists_id/pfet.spice b/models/xyce/testing/regression/mos_hv_id/device_netlists_id/pfet.spice new file mode 100644 index 00000000..a1d63544 --- /dev/null +++ b/models/xyce/testing/regression/mos_hv_id/device_netlists_id/pfet.spice @@ -0,0 +1,37 @@ +*************************** +** pfet_03v3_t_id +*************************** +* Copyright 2023 Efabless Corporation +* +* 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. +vds D_tn 0 dc {{vds_val}} +vgs G_tn 0 dc -10 +Vbs B_tn 0 dc {{vbs_val}} + +.STEP TEMP {{temp}} {{temp}} {{temp}} + +xmp1 D_tn G_tn 0 B_tn {{device}} W = {{width}}u L = {{length}}u + +**** begin architecture code +.DC {{sweeps}} + +.print DC FORMAT=CSV file={{result_path}} v(D_tn) v(G_tn) v(B_tn) {I(Vds)} + +** library calling + +.include {{model_design_path}} +.lib {{model_card_path}} {{corner}} + +**** end architecture code + +.end diff --git a/models/xyce/testing/regression/mos_hv_id/models_regression.py b/models/xyce/testing/regression/mos_hv_id/models_regression.py new file mode 100644 index 00000000..7a0d7c22 --- /dev/null +++ b/models/xyce/testing/regression/mos_hv_id/models_regression.py @@ -0,0 +1,364 @@ +# Copyright 2023 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. + +""" +Usage: + models_regression.py [--num_cores=] [--meas_result=] + + -h, --help Show help text. + -v, --version Show version. + --num_cores= Number of cores to be used by simulator + --meas_result= Measurement to be tested (Allowed: id, rds). [default: id] +""" + +from docopt import docopt +import pandas as pd +import numpy as np +from subprocess import check_call +from jinja2 import Template +import concurrent.futures +import shutil +import glob +import os +import logging + + +def check_xyce_version(): + """ + check_xyce_version checks Xyce version and makes sure it would work with the models. + """ + # ======= Checking Xyce ======= + Xyce_v_ = os.popen("Xyce -v 2> /dev/null").read().replace("\n", "") + if Xyce_v_ == "": + logging.error("Xyce is not found. Please make sure Xyce is installed.") + exit(1) + elif "7.5" not in Xyce_v_: + logging.error("Xyce version 7.5 is required.") + exit(1) + + logging.info(f"Your Xyce version is: {Xyce_v_}") + + +def simulate_device(netlist_path: str): + """ + Call simulation commands to perform simulation. + Args: + file_name (str): Netlist cir for run. + Returns: + int: Return code of the simulation. 0 if success. Non-zero if failed. + """ + + return check_call(f"Xyce {netlist_path} -hspice-ext all > {netlist_path}.log 2>&1", + shell=True) + + +def run_sim(dirpath: str, device: str, meas_out_result: str, + width: str, length: float, corner: float, + temp: float, const_var: str, const_var_val: float, + sweeps: str) -> dict: + """ + Function to run simulation for all data points per each variation. + + Parameters + ---------- + dirpath : str or Path + Path to the run results directory + device : str + Device used in regression test + meas_out_result : str + Measurement selected to be test for the current regression. + width: float + Width value for the current run + length: float + length value for the current run + temp: float + temp value for the current run + const_var: str + Name of constant voltage for the current run + const_var_val: float + Value of constant voltage for the current run + sweeps: str + Str that holds all voltage sweeps for the current run + Returns + ------- + info(dict): + Dataframe contains results for the current run + """ + + # Get model card path + regression_dir = os.path.dirname(os.path.abspath(__file__)) + models_dir = os.path.dirname(os.path.dirname(os.path.dirname(regression_dir))) + model_card_path = os.path.join(models_dir, "smbb000149.xyce") + model_design_path = os.path.join(models_dir, "design.xyce") + + # Select desired nelist templete to be used in the current run + device_group_netlist = "nfet" if "nfet" in device else "pfet" + + netlist_tmp = os.path.join(f"device_netlists_{meas_out_result}", f"{device_group_netlist}.spice") + + # Preparing output directory at which results will be added + dev_netlists_path = os.path.join(dirpath, f"{device}_netlists") + os.makedirs(dev_netlists_path, exist_ok=True) + + sp_file_name = f"netlist_w{width}_l{length}_t{temp}_{const_var}{const_var_val}_{meas_out_result}.spice" + netlist_path = os.path.join(dev_netlists_path, sp_file_name) + + sim_file_name = f"simulated_w{width}_l{length}_t{temp}_{const_var}{const_var_val}_{meas_out_result}.csv" + result_path = os.path.join(dev_netlists_path, sim_file_name) + + info = {} + info["device"] = device + info["temp"] = temp + info["corner"] = corner + info["length"] = length + info["width"] = width + + # Check constant voltage values + vbs_val = const_var_val if const_var == "vbs" else 0 + vds_val = const_var_val if const_var == "vds" else 10 + + # Generating netlist templates for all variations + with open(netlist_tmp) as f: + tmpl = Template(f.read()) + with open(netlist_path, "w") as netlist: + netlist.write( + tmpl.render( + device=device, + width=width, + length=length, + temp=temp, + corner=corner, + sweeps=sweeps, + vds_val=vds_val, + vbs_val=vbs_val, + result_path=result_path, + model_card_path=model_card_path, + model_design_path=model_design_path, + ) + ) + + # Running xyce for each netlist + logging.info(f"Running simulation for {device} at w={width}, l={length}, temp={temp}, sweeps={sweeps}, out={meas_out_result}") + + # calling simulator to run netlist and write its results + try: + simulate_device(netlist_path) + mos_id_rd = result_path if os.path.exists(result_path) else np.nan + except Exception: + mos_id_rd = np.nan + + # Cleaning output csv files from simulation results and + ## fromating columns to match what we have in measurement + if os.path.exists(result_path) and os.path.isfile(result_path): + result_df = pd.read_csv(result_path) + # Renaming output columns with proper names + if "nfet" in device: + result_df.rename( + columns={ + "V(D_TN)": "vds", + "V(G_TN)": "vgs", + "V(B_TN)": "vbs", + "{-I(VDS)}": "id", + "{1/N(xmn1:m0:gds)}": "rds", + }, + inplace=True, + ) + if "pfet" in device: + result_df.rename( + columns={ + "V(D_TN)": "vds", + "V(G_TN)": "vgs", + "V(B_TN)": "vbs", + "{I(VDS)}": "id", + "{1/N(xmn1:m0:gds)}": "rds", + }, + inplace=True, + ) + + # Adding columns for all variations per each run + result_df["W (um)"] = width + result_df["L (um)"] = length + result_df["corner"] = corner + result_df["temp"] = temp + + # Writing output in clean format in same csv path of resutls + result_df.to_csv(result_path, index=False, header=True, sep=",") + + info["id_rds_sim"] = mos_id_rd + + return info + + +def run_sims( + df: pd.DataFrame, dirpath: str, device: str, meas_out_result: str, +) -> pd.DataFrame: + """ + Function to run all simulations for all data points and generating results in proper format. + + Parameters + ---------- + df : pd.DataFrame + Data frame contains all sweep points will be used in simulation + dirpath : str or Path + Output directory for the regresion results + device: str + Name of device for the current run + meas_out_result: str + Measurement selected to be test for the current regression. + Returns + ------- + df: Pd.DataFrame + Dataframe contains all simulation results + """ + + results = [] + with concurrent.futures.ThreadPoolExecutor(max_workers=workers_count) as executor: + futures_list = [] + for j, row in df.iterrows(): + futures_list.append( + executor.submit( + run_sim, + dirpath, + device, + meas_out_result, + row["W (um)"], + row["L (um)"], + row["corner"], + row["temp"], + row["const_var"], + row["const_var_val"], + row["sweeps"], + ) + ) + + for future in concurrent.futures.as_completed(futures_list): + try: + data = future.result() + results.append(data) + except Exception as exc: + logging.info("Test case generated an exception: %s" % (exc)) + + # Get all simulation generated csv files + results_path = os.path.join(dirpath, f"{device}_netlists") + run_results_csv = glob.glob(f"{results_path}/*.csv") + + # Merging all simulation results in one dataframe + df = pd.concat([pd.read_csv(f) for f in run_results_csv], ignore_index=True) + return df + + +def main(meas_out_result): + """ + Main function for Fets regression for GF180MCU models + Parameters + ---------- + meas_out_result : str + Measurement selected to be test for the current regression. + """ + + ## Check xyce version + check_xyce_version() + + # pandas setup + pd.set_option("display.max_columns", None) + pd.set_option("display.max_rows", None) + pd.set_option("max_colwidth", None) + pd.set_option("display.width", 1000) + pd.options.mode.chained_assignment = None + + main_regr_dir = f"run_mos_{meas_out_result}_regr" + + devices = [ + "nfet_10v0_asym", + "pfet_10v0_asym", + ] + + # Simulate all data points for each device + for dev in devices: + dev_path = os.path.join(main_regr_dir, dev) + + # Making sure to remove old runs + if os.path.exists(dev_path) and os.path.isdir(dev_path): + shutil.rmtree(dev_path) + + os.makedirs(dev_path, exist_ok=False) + + logging.info("######" * 10) + logging.info(f"# Checking Device {dev}") + + # Loading sweep file used in measurements to be used in simulation for regression + sweeps_file = f"../../../../180MCU_SPICE_DATA_clean/gf180mcu_data/MOS_iv/{dev}_sweeps_{meas_out_result}.csv" + + if not os.path.exists(sweeps_file) or not os.path.isfile(sweeps_file): + logging.error("There is no measured data to be used in simulation, please recheck") + logging.error(f"{sweeps_file} file doesn't exist, please recheck") + exit(1) + + df_sweeps = pd.read_csv(sweeps_file) + logging.info(f"Data points used in simulation for {dev}:\n {df_sweeps}") + + sim_df = run_sims(df_sweeps, dev_path, dev, meas_out_result) + sim_df.drop_duplicates(inplace=True) + + sim_df_columns = ["W (um)", "L (um)", "corner", "temp", "vds", "vgs", "vbs", f"{meas_out_result}"] + sim_df = sim_df.reindex(columns=sim_df_columns) + + sim_df.to_csv(f"{dev_path}/sim_results_{dev}.csv", index=False) + + logging.info(f"# Device {dev} number of simulated datapoints for {meas_out_result} : {len(sim_df)} ") + + # Verify regression results + if not sim_df[f"{meas_out_result}"].isnull().values.any(): + logging.info(f"# Device {dev} for {meas_out_result} simulation has passed.") + else: + logging.error( + f"# Device {dev} {meas_out_result} simulation has failed regression." + ) + logging.error( + f"#Failed regression for {dev}-{meas_out_result} analysis." + ) + exit(1) + +# ================================================================ +# -------------------------- MAIN -------------------------------- +# ================================================================ + + +if __name__ == "__main__": + + # Args + arguments = docopt(__doc__, version="MODELS-REGRESSION: 0.1") + workers_count = ( + os.cpu_count() * 2 + if arguments["--num_cores"] is None + else int(arguments["--num_cores"]) + ) + + meas_out_result = arguments["--meas_result"] + + logging.basicConfig( + level=logging.DEBUG, + handlers=[ + logging.StreamHandler(), + ], + format="%(asctime)s | %(levelname)-7s | %(message)s", + datefmt="%d-%b-%Y %H:%M:%S", + ) + + if meas_out_result not in ["id", "rds"]: + logging.error(f"{meas_out_result} is not supported, allowed measurements for Fets are [id, rds], please recheck") + exit(1) + + # Calling main function + main(meas_out_result)