Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding comparison against simulation for nfet #26

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "technology/skywater-pdk-libs-sky130_fd_pr"]
path = technology/skywater-pdk-libs-sky130_fd_pr
url = https://github.com/google/skywater-pdk-libs-sky130_fd_pr.git
4 changes: 4 additions & 0 deletions notebooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Comapring simulated data with raw data

Result of comparison done in "sky130_plot_simdata_compare.ipynb" notebook indicates that simulated data is almost equal to raw data except for loc=5290

Large diffs are not rendered by default.

70 changes: 70 additions & 0 deletions notebooks/raw_gen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import pathlib
import DMT.core
import pandas as pd
import glob
from subprocess import check_call
from docopt import docopt

# reading raw data files
curve_type = ["ID_VDS", "ID_VGS"]
cells_dir = pathlib.Path("../sky130_fd_pr/cells")
cell_dir = cells_dir / "nfet_01v8"
data_files = glob.glob(f"{cell_dir}/*.mdm")

sel_files_vd = dict()
sel_files_vg = dict()

for file in data_files:

part1 = file.split("(")[1]
part2 = part1.split(")")[0]
data = part2.split("_")

if "D3" in data:
loc = int(data[0])
s_pin = 0
d_pin = int(data[1])
else:
loc = int(data[0])
s_pin = int(data[1])
d_pin = int(data[2])

if "IDVD" in file:
sel_files_vd[file] = ((loc, s_pin, d_pin))
elif "IDVG" in file:
sel_files_vg[file] = ((loc, s_pin, d_pin))

total_sel_files = [sel_files_vd, sel_files_vg]
for sel_files in total_sel_files:
index = 1
raw_df = pd.DataFrame()
for mdm_path, name in sel_files.items():
mdm_path = pathlib.Path(mdm_path)
measurement = DMT.core.DutMeas(
database_dir=None,
dut_type=DMT.core.DutType.device,
name=mdm_path.stem,
reference_node="E",
)

measurement.add_data(pathlib.Path(mdm_path), key=mdm_path.stem)
df_read = measurement.data[mdm_path.stem]
df_read["VGS"] = df_read["VG"] - df_read["VS"]
df_read["VSB"] = df_read["VS"] - df_read["VB"]
df_read["VDS"] = df_read["VD"] - df_read["VS"]
df_read.drop(columns=["VG", "VS", "VD", "VB"], inplace=True)
df_read["loc"] = name[0]
df_read["s_pin"] = name[1]
df_read["d_pin"] = name[2]

if index == 1:
raw_df = df_read
else:
raw_df = pd.concat([raw_df, df_read])

index += 1

if sel_files == sel_files_vd:
raw_df.to_csv(f"{cell_dir}/raw_data_idvd.csv")
elif sel_files == sel_files_vg:
raw_df.to_csv(f"{cell_dir}/raw_data_idvg.csv")
303 changes: 303 additions & 0 deletions notebooks/sky130_plot_simdata_compare.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import pandas as pd\n",
"import ipywidgets as widgets\n",
"from matplotlib.ticker import EngFormatter\n",
"from matplotlib.colors import ListedColormap\n",
"import matplotlib as mpl\n",
"import re\n",
"import numpy as np\n",
"from jinja2 import Template\n",
"from subprocess import check_call\n",
"import concurrent.futures\n",
"import multiprocessing as mp\n",
"import glob\n",
"import pathlib"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Reading Pad Documentation csv file to extract parameters\n",
"doc_df = pd.read_csv(\"pad_documentation/Pad_documentation _for _SKY130_ MPW_Manufacturing.csv\")\n",
"nfet_01v8_df = doc_df.loc[doc_df[\"New Style Name\"] == \"nfet_01v8\"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# curve_type must be one of ['ID_VDS', 'ID_VGS']\n",
"curve_type = \"ID_VDS\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Generating sim data\n",
"def simulate_device(netlist_path):\n",
" check_call(f\"ngspice -b {netlist_path} -o output.log \", shell= True)\n",
"\n",
"workers_count = 2 * mp.cpu_count()\n",
"nmos_netlist = \"templates/nfet_01v8_template.cir\"\n",
"loc_sweep = list()\n",
"s_pin_sweep = list()\n",
"d_pin_sweep = list()\n",
"\n",
"if curve_type == \"ID_VDS\":\n",
" sim_cmd = \"DC vds 0 1.8 0.05\"\n",
" sweep_range = \"start=0 stop=1.8 step=0.36\"\n",
" v_sweep = \"vgs\"\n",
"\n",
"elif curve_type == \"ID_VGS\":\n",
" sim_cmd = \"DC vgs 0 1.8 0.05\"\n",
" sweep_range = \"start=0.1 stop=1.8 step=1.7\"\n",
" v_sweep = \"vds\"\n",
"\n",
"with concurrent.futures.ProcessPoolExecutor(max_workers=workers_count) as executor:\n",
" for i, row in nfet_01v8_df.iterrows():\n",
" sim_data = row[\"Description\"]\n",
" subs = [';', 'nshort', 'in DNW', 'contact-gate=\\d+.\\d+um']\n",
" for element in subs:\n",
" sim_data = re.sub(element, ' ' ,sim_data)\n",
"\n",
" # get pins loc for each file\n",
" pin1 = int()\n",
" pin2 = int()\n",
" for c in row.index:\n",
" if row[c] == \"s\":\n",
" pin1 = int(re.sub(\"Pin \", '', c))\n",
" elif row[c] == \"d\":\n",
" pin2 = int(re.sub(\"Pin \", '', c))\n",
"\n",
" # saving sweep data\n",
" s_pin_sweep.append(int(pin1))\n",
" d_pin_sweep.append(int(pin2))\n",
" if row[\"Mod #\"] == row[\"Mod #\"]:\n",
" loc_sweep.append(int(row[\"Mod #\"]))\n",
" \n",
" # get width, length, multiplier\n",
" width = re.findall(\"w=\\d*.\\d*\", sim_data)[0]\n",
" width = re.sub(\"=\", \"\", width)\n",
" length = re.findall(\"l=\\d*.\\d*\", sim_data)[0]\n",
" length = re.sub(\"=\", \"\", length)\n",
" multiplier = re.findall(\"m=\\d*\", sim_data)[0]\n",
" multiplier = re.sub(\"=\", \"\", multiplier)\n",
"\n",
" # naming circuit, csv files\n",
" file_name = f\"nfet_01v8_{loc_sweep[-1]}_{pin1}_{pin2}\"\n",
" netlist_path = f\"{file_name}.cir\"\n",
" csv_path = f\"{file_name}.csv\"\n",
"\n",
" with open(nmos_netlist) as f:\n",
" tmpl = Template(f.read())\n",
" with open(netlist_path, \"w\") as netlist:\n",
" netlist.write(\n",
" tmpl.render(\n",
" csv_path=csv_path,\n",
" dimensions=sim_data,\n",
" sim_cmd=sim_cmd,\n",
" sweep_range=sweep_range,\n",
" v_sweep=v_sweep,\n",
" )\n",
" )\n",
" \n",
" executor.submit(simulate_device, netlist_path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# clean unused files\n",
"check_call(\"rm -f nfet_*.cir\", shell=True)\n",
"check_call(\"rm -f output.log\", shell=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# reading raw data generated from \"raw_gen.py\" script\n",
"data_dir = pathlib.Path(\"../sky130_fd_pr/cells\")\n",
"data_dir = data_dir / \"nfet_01v8\"\n",
"if curve_type == \"ID_VDS\":\n",
" raw_df = pd.read_csv(f\"{data_dir}/raw_data_idvd.csv\")\n",
"elif curve_type == \"ID_VGS\":\n",
" raw_df = pd.read_csv(f\"{data_dir}/raw_data_idvg.csv\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# widgets settings \n",
"loc_sweep = [*set(loc_sweep)]\n",
"s_pin_sweep = [*set(s_pin_sweep)]\n",
"d_pin_sweep = [*set(d_pin_sweep)]\n",
"\n",
"loc = widgets.Dropdown(\n",
" options=loc_sweep,\n",
" value=loc_sweep[0],\n",
" description='loc:',\n",
" disabled=False,\n",
")\n",
"s_pin = widgets.Dropdown(\n",
" options=s_pin_sweep,\n",
" value=s_pin_sweep[0],\n",
" description='s-pin:',\n",
" disabled=False,\n",
")\n",
"d_pin = widgets.Dropdown(\n",
" options=d_pin_sweep,\n",
" value=d_pin_sweep[0],\n",
" description='d-pin:',\n",
" disabled=False,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"blue_range = ListedColormap(mpl.cm.Blues(np.linspace(0.3, 0.8, 256)))\n",
"red_range = ListedColormap(mpl.cm.Reds(np.linspace(0.3, 0.8, 256)))\n",
"\n",
"sim_files = glob.glob(f\"nfet_01v8*.csv\")\n",
"def get_sim_df(loc, s_pin, d_pin):\n",
" csv_file = str()\n",
" for file in sim_files:\n",
" file_sp = file.split(\"_\")\n",
" d_read = int(file_sp[4].split(\".\")[0])\n",
" \n",
" if loc == int(file_sp[2]) and s_pin == int(file_sp[3]) and d_pin == d_read:\n",
" csv_file = file\n",
" break\n",
" if csv_file != \"\":\n",
" sim_df = pd.read_csv(csv_file, delimiter=r\"\\s+\") \n",
" sim_df.drop(\n",
" sim_df.columns[[x for x in range(0, sim_df.shape[1]) if x % 2 == 0 and x != 0]],\n",
" inplace=True,\n",
" axis=1,\n",
" )\n",
" sim_df.columns = [\n",
" \"VDS\",\n",
" \"VGS\",\n",
" \"VSB\",\n",
" \"ID\",\n",
" \"GM\",\n",
" ]\n",
" else:\n",
" raise(Exception(\"There is no data for selected loc, s-pin, d-pin\"))\n",
"\n",
" return sim_df\n",
"\n",
"# plotting data:\n",
"def plot(loc, s_pin, d_pin):\n",
" tdf_sim= get_sim_df(loc, s_pin, d_pin)\n",
" tdf_raw = raw_df.loc[(raw_df[\"loc\"] == loc) & (raw_df[\"s_pin\"] == s_pin)\n",
" & (raw_df[\"d_pin\"] == d_pin)]\n",
" vsb_values = [*set(tdf_raw[\"VSB\"].tolist())]\n",
" \n",
" for VSB in vsb_values:\n",
" tdf_sim1 = tdf_sim.loc[(tdf_sim[\"VSB\"] == VSB)]\n",
" tdf_raw1 = tdf_raw.loc[(tdf_raw[\"VSB\"] == VSB)]\n",
" id_df_sim = tdf_sim1[[\"VDS\", \"VGS\", \"ID\"]].copy()\n",
" id_df_raw = tdf_raw1[[\"VDS\", \"VGS\", \"ID\"]].copy()\n",
"\n",
" if curve_type == \"ID_VDS\":\n",
" id_df_sim.set_index(\"VDS\", inplace=True)\n",
" id_df_sim.sort_index(inplace=True)\n",
" id_df_raw.set_index(\"VDS\", inplace=True)\n",
" id_df_raw.sort_index(inplace=True)\n",
" id_df_sim = pd.pivot_table(id_df_sim.reset_index(),\n",
" values=\"ID\",\n",
" index=\"VDS\",\n",
" columns=[\"VGS\"])\n",
" ax = id_df_sim.plot(colormap=blue_range, grid=True, figsize=(12,6))\n",
" id_df_raw = pd.pivot_table(id_df_raw.reset_index(),\n",
" values=\"ID\",\n",
" index=\"VDS\",\n",
" columns=[\"VGS\"])\n",
" id_df_raw.plot(colormap=red_range, ax=ax)\n",
" plt.legend(title= \"VGS\", loc=0)\n",
" volt_formatter = EngFormatter(unit='V')\n",
" amp_formatter = EngFormatter(unit='A')\n",
" ax.xaxis.set_major_formatter(volt_formatter)\n",
" ax.yaxis.set_major_formatter(amp_formatter)\n",
" plt.title(f\"IDS vs VDS at VSB={VSB} - Simulated (Blues) - Measured (Reds)\")\n",
" elif curve_type == \"ID_VGS\":\n",
" id_df_sim.set_index(\"VGS\", inplace=True)\n",
" id_df_sim.sort_index(inplace=True)\n",
" id_df_raw.set_index(\"VGS\", inplace=True)\n",
" id_df_raw.sort_index(inplace=True)\n",
" id_df_sim = pd.pivot_table(id_df_sim.reset_index(),\n",
" values=\"ID\",\n",
" index=\"VGS\",\n",
" columns=[\"VDS\"])\n",
" ax = id_df_sim.plot(colormap=blue_range, grid=True, figsize=(12,6))\n",
" id_df_raw = pd.pivot_table(id_df_raw.reset_index(),\n",
" values=\"ID\",\n",
" index=\"VGS\",\n",
" columns=[\"VDS\"])\n",
" id_df_raw.plot(colormap=red_range, ax=ax)\n",
" plt.legend(title= \"VDS\", loc=0)\n",
" volt_formatter = EngFormatter(unit='V')\n",
" amp_formatter = EngFormatter(unit='A')\n",
" ax.xaxis.set_major_formatter(volt_formatter)\n",
" ax.yaxis.set_major_formatter(amp_formatter)\n",
" plt.title(f\"IDS vs VGS VSB={VSB} - Simulated (Blues) - Measured (Reds)\")\n",
" plt.ylabel(\"IDS\")\n",
" \n",
" \n",
"# interactive mode\n",
"widgets.interact(plot, loc=loc, s_pin=s_pin, d_pin=d_pin)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "py3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.6"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}
Loading