blob: 23128ee7e6c8fd74c6cf0a16acc98aa33653f66a [file] [log] [blame]
# Copyright 2022 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.
"""
Usage:
smoke_test.py [--num_cores=<num>]
-h, --help Show help text.
-v, --version Show version.
--num_cores=<num> Number of cores to be used by simulator
"""
import re
from docopt import docopt
import pandas as pd
import os
from jinja2 import Template
import concurrent.futures
import itertools
import datetime
import warnings
import subprocess
warnings.simplefilter(action='ignore', category=FutureWarning)
def call_simulator(file_name):
"""Call simulation commands to perform simulation.
Args:
file_name (str): Netlist file name.
"""
p = subprocess.Popen(f"ngspice -b -a {file_name} > {file_name}.log 2>&1", shell=True, executable='/bin/bash')
p.wait()
def get_sizes(models_path):
with open(models_path, "r") as f:
device_model = f.read()
dimensions = re.findall(f"\.model nmos_3p3.*\n.*\n\+lmin.*= (.*\S).*\n.*\n\+wmin.*= (.*\S)", device_model)
return dimensions[0:16]
def get_results(run_path, sizes, temp, corner):
netlist_tmp = f"./inv_ng.spice"
width = float(sizes[1]) * 1000000
width_p = width * 1.5
length = float(sizes[0]) * 1000000
# AD = width * 0.24
# AD_p = AD * 1.5
# PD = 2 * (width + 0.24)
# PD_p = width + PD
# AS = AD
# PS = PD
with open(netlist_tmp) as f:
tmpl = Template(f.read())
os.makedirs(f"{run_path}/netlists",exist_ok=True)
os.makedirs(f"{run_path}/simulation",exist_ok=True)
netlist_path = f"{run_path}/netlists/inv_W{width}_L{length}_T{temp}_{corner}.spice"
with open(netlist_path, "w") as netlist:
netlist.write(tmpl.render(corner = corner, width = width,length = length, temp = temp , run_path = run_path, width_p = width_p))#, AD = AD , PD = PD , AS = AS , PS = PS, AD_p = AD_p, PD_p = PD_p ))
call_simulator(netlist_path)
# Writing simulated data
df_simulated = pd.read_csv(f"{run_path}/simulation/inv_W{width}_L{length}_T{temp}_{corner}.csv",header=None, delimiter=r"\s+")
return [f"W{width}_L{length}_T{temp}_{corner}",df_simulated.iloc[-1, -1]]
def main():
models_path = "../../sm141064.ngspice"
temps = ["25","-40","125"]
corners = ["typical","ff","ss","fs","sf"]#,"stat"]
time = f"{datetime.datetime.now()}".replace(" ", "_")
run_path = f"../run_smoke_{time}"
os.makedirs(run_path,exist_ok=True)
sizes = get_sizes(models_path)
results = []
all_combs = list(itertools.product(sizes, temps, corners))
with concurrent.futures.ThreadPoolExecutor(max_workers=workers_count) as executor:
# Start the load operations and mark each future with its URL
future_list = [executor.submit(get_results, run_path, comb[0], comb[1], comb[2]) for comb in all_combs]
for future in concurrent.futures.as_completed(future_list):
try:
results.append(future.result())
except Exception as exc:
print('Generated an exception: %s' % (exc))
df_results = pd.DataFrame(results)
df_results.columns = ["run","tpd_result"]
df_results.to_csv(f"{run_path}/final_results.csv",index= False)
print (df_results)
# # ================================================================
# -------------------------- MAIN --------------------------------
# ================================================================
if __name__ == "__main__":
# Args
arguments = docopt(__doc__, version='smoke_test: 0.1')
workers_count = os.cpu_count()*2 if arguments["--num_cores"] == None else int(arguments["--num_cores"])
# Calling main function
main()