IHP-AnalogAcademy/modules/module_0_foundations/scripting/gmid_test.ipynb

440 lines
18 KiB
Plaintext

{
"cells": [
{
"cell_type": "code",
"execution_count": 11,
"id": "9f325daf-eb3b-4cf7-8ffe-b9b94e7f66ea",
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"from mosplot.plot import load_lookup_table, Mosfet, Expression\n",
"import ipywidgets as widgets\n",
"from ipywidgets import interactive\n",
"from ipywidgets import interactive_output, HBox, VBox\n",
"import matplotlib.ticker as ticker "
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "b5b31aca-47bf-4461-8e50-16c20f03b337",
"metadata": {},
"outputs": [],
"source": [
"lookup_table_nmos = load_lookup_table('../sg13_nmos_lv_LUT.npz')\n",
"lookup_table_pmos = load_lookup_table('../sg13_pmos_lv_LUT.npz')"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "743dc381-0d35-4aa9-847c-c42c80c17786",
"metadata": {},
"outputs": [],
"source": [
"nmos = Mosfet(lookup_table=lookup_table_nmos, mos=\"sg13_lv_nmos \", vbs=0.0, vds=0.6)\n",
"pmos = Mosfet(lookup_table=lookup_table_pmos, mos=\"sg13_lv_pmos\", vbs=0.0, vds=-0.6, vgs=(-1.2, -0.15))"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "b27d5fca-3436-4df7-895f-f6a4bbd7a80d",
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [],
"source": [
"def plot_data_vs_data(x_values, y_values, z_values, length, x_axis_name, y_axis_name='y', y_multiplier=1, log=False):\n",
" x_values_flat = np.array(x_values).flatten()\n",
" y_values_flat = np.array(y_values, dtype=np.float64).flatten()\n",
" z_values_flat = np.array(z_values, dtype=np.float64).flatten()\n",
" \n",
" length_arr = np.array(length)\n",
" \n",
" length_flat = length_arr.flatten()\n",
" \n",
"\n",
" unique_lengths = np.unique(length_flat)\n",
" unique_lengths_in_micro = unique_lengths * 1e6\n",
"\n",
" def update_plot(selected_length, x_value=None, y_value=None):\n",
" plt.figure(figsize=(12, 8)) # Make the figure wider (adjust as needed)\n",
"\n",
" if selected_length == \"Show All\":\n",
" mask = np.ones_like(length_flat, dtype=bool)\n",
" else:\n",
" selected_length_in_micro = float(selected_length.replace(' μm', ''))\n",
" tolerance = 0.01 # Tighten the tolerance to avoid unwanted data points\n",
" mask = np.abs(length_flat * 1e6 - selected_length_in_micro) < tolerance\n",
"\n",
" # Apply the mask to the data\n",
" x_values_for_length = x_values_flat[mask]\n",
" y_values_for_length = y_values_flat[mask] * y_multiplier\n",
" z_values_for_length = z_values_flat[mask]\n",
" length_for_length = length_flat[mask] * 1e6\n",
"\n",
" if selected_length == \"Show All\":\n",
" for length_value in np.unique(length_for_length):\n",
" mask_all = (length_for_length == length_value)\n",
" plt.plot(x_values_for_length[mask_all], y_values_for_length[mask_all])\n",
"\n",
" min_length = np.min(unique_lengths_in_micro)\n",
" max_length = np.max(unique_lengths_in_micro)\n",
" plt.title(f'{y_axis_name} vs {x_axis_name} (Length from {min_length:.2f} μm to {max_length:.2f} μm)')\n",
"\n",
" else:\n",
" plt.plot(x_values_for_length, y_values_for_length)\n",
" plt.title(f'{y_axis_name} vs {x_axis_name} for {selected_length}')\n",
"\n",
" plt.xlabel(f'{x_axis_name}')\n",
" plt.ylabel(f'{y_axis_name}')\n",
"\n",
" if log:\n",
" plt.yscale('log')\n",
" plt.gca().yaxis.set_major_locator(ticker.LogLocator(base=10, subs=[], numticks=10))\n",
" plt.gca().yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, _: f'$10^{int(np.log10(x))}$'))\n",
" plt.ylabel(f'{y_axis_name} (Log Base 10)')\n",
"\n",
" if y_value is not None and x_value_widget.disabled:\n",
" closest_index = np.abs(y_values_for_length - y_value).argmin()\n",
" closest_x = x_values_for_length[closest_index]\n",
" closest_y = y_values_for_length[closest_index]\n",
" corresponding_z = z_values_for_length[closest_index]\n",
"\n",
" plt.scatter(closest_x, closest_y, color='blue', label=f'Point ({closest_x:.2f}, {closest_y:.2f})')\n",
" z_value_widget.value = corresponding_z\n",
" print(f\"The corresponding {x_axis_name} value for {y_axis_name} = {closest_y:.2f} is: {closest_x:.2f}\")\n",
" elif x_value is not None and y_value_widget.disabled:\n",
" closest_index = np.abs(x_values_for_length - x_value).argmin()\n",
" closest_x = x_values_for_length[closest_index]\n",
" closest_y = y_values_for_length[closest_index]\n",
" corresponding_z = z_values_for_length[closest_index]\n",
"\n",
" plt.scatter(closest_x, closest_y, color='red', label=f'Point ({closest_x:.2f}, {closest_y:.2f})')\n",
" z_value_widget.value = corresponding_z\n",
" print(f\"The corresponding {y_axis_name} value for {x_axis_name} = {closest_x:.2f} is: {closest_y:.2f}\")\n",
"\n",
" plt.grid(True)\n",
" plt.legend()\n",
" plt.show()\n",
"\n",
" dropdown_options = [\"Show All\"] + [f'{length:.2f} μm' for length in unique_lengths_in_micro]\n",
" length_widget = widgets.Dropdown(\n",
" options=dropdown_options,\n",
" value=dropdown_options[0],\n",
" description='Length:',\n",
" layout=widgets.Layout(width='500px') # Make the dropdown wider\n",
" )\n",
"\n",
" x_value_widget = widgets.FloatText(\n",
" value=np.mean(x_values_flat),\n",
" description=f\"{x_axis_name}:\",\n",
" disabled=False,\n",
" layout=widgets.Layout(width='300px', margin='0 40px 0 0'), # Push input boxes more to the right\n",
" description_width='150px' # Smaller description width\n",
" )\n",
"\n",
" y_value_widget = widgets.FloatText(\n",
" value=None,\n",
" description=f\"{y_axis_name}:\",\n",
" disabled=True,\n",
" layout=widgets.Layout(width='300px', margin='0 40px 0 0'), # Push input boxes more to the right\n",
" description_width='150px' # Smaller description width\n",
" )\n",
"\n",
" z_value_widget = widgets.FloatText(\n",
" value=None,\n",
" description=f\" Vgs:\",\n",
" disabled=True,\n",
" layout=widgets.Layout(width='300px', margin='0 40px 0 0'), # Push input boxes more to the right\n",
" description_width='150px' # Smaller description width\n",
" )\n",
"\n",
" select_x_or_y_widget = widgets.Checkbox(\n",
" value=True,\n",
" description=f\"{x_axis_name} (uncheck for {y_axis_name})\",\n",
" layout=widgets.Layout(width='300px') # Make the checkbox wider\n",
" )\n",
"\n",
" def toggle_x_or_y(change):\n",
" if change['new']:\n",
" x_value_widget.disabled = False\n",
" y_value_widget.disabled = True\n",
" else:\n",
" x_value_widget.disabled = True\n",
" y_value_widget.disabled = False\n",
"\n",
" select_x_or_y_widget.observe(toggle_x_or_y, names='value')\n",
"\n",
" output = interactive_output(update_plot, {\n",
" 'selected_length': length_widget,\n",
" 'x_value': x_value_widget,\n",
" 'y_value': y_value_widget\n",
" })\n",
"\n",
" display(VBox([length_widget, select_x_or_y_widget, HBox([x_value_widget, y_value_widget]), z_value_widget, output]))\n",
"\n",
"\n",
"\n",
"def tile_length_to_match_data(length_array, data_array):\n",
" length_array = np.array(length_array).flatten() \n",
" data_shape = data_array.shape \n",
" \n",
" if length_array.size == data_shape[0]:\n",
" # length matches number of rows, repeat along columns\n",
" return np.tile(length_array.reshape(-1, 1), (1, data_shape[1]))\n",
" elif length_array.size == data_shape[1]:\n",
" # length matches number of columns, repeat along rows\n",
" return np.tile(length_array.reshape(1, -1), (data_shape[0], 1))\n",
" else:\n",
" raise ValueError(f\"Length array size {length_array.size} does not match any dimension of data shape {data_shape}\")\n",
"\n",
"\n",
" \n",
"def display_resistance(ro_value):\n",
" \"\"\"Determine the resistance value and its unit.\"\"\"\n",
" if ro_value < 1e3:\n",
" return ro_value, \"Ω\"\n",
" elif ro_value < 1e6:\n",
" return ro_value / 1e3, \"kΩ\"\n",
" elif ro_value < 1e9:\n",
" return ro_value / 1e6, \"MΩ\"\n",
" else:\n",
" return ro_value / 1e9, \"GΩ\"\n",
"\n",
"def display_current(Id_value):\n",
" \"\"\"Determine the current value and its unit.\"\"\"\n",
" if Id_value < 1e-6:\n",
" return Id_value * 1e9, \"nA\" # Convert to nA\n",
" elif Id_value < 1e-3:\n",
" return Id_value * 1e6, \"μA\" # Convert to μA\n",
" else:\n",
" return Id_value * 1e3, \"mA\" # Convert to mA\n",
" \n",
"def dB_to_linear(av_db):\n",
" return 10 ** (av_db / 20)\n",
"\n",
"\n",
"def determine_inversion_region(gm_id_value, device_type):\n",
" \"\"\"Determine the inversion region based on gm/id value for NMOS or PMOS.\"\"\"\n",
" if device_type == 'nmos':\n",
" if gm_id_value > 20:\n",
" return \"Weak Inversion\"\n",
" elif 10 < gm_id_value <= 20:\n",
" return \"Moderate Inversion\"\n",
" else:\n",
" return \"Strong Inversion\"\n",
" elif device_type == 'pmos':\n",
" if gm_id_value > 20:\n",
" return \"Weak Inversion\"\n",
" elif 10 < gm_id_value <= 20:\n",
" return \"Moderate Inversion\"\n",
" else:\n",
" return \"Strong Inversion\"\n",
" else:\n",
" raise ValueError(\"Invalid device type. Use 'nmos' or 'pmos'.\")\n",
" \n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "fc95a0ff-ba36-46ce-9fa1-64d6d2d7770f",
"metadata": {},
"outputs": [],
"source": [
"### ------ automated flattening based on shape ------### \n",
"length_2d_pmos = tile_length_to_match_data(pmos.length, pmos.extracted_table['gm'])\n",
"length_2d_nmos = tile_length_to_match_data(nmos.length, nmos.extracted_table['gm'])"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "0cec9718-17e7-42bb-bbfb-732d1c90bcb2",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'id': array([[3.76624204e-10, 6.01959060e-09, 9.87071189e-08, 1.60543800e-06,\n",
" 1.93245942e-05, 1.00782425e-04, 3.04098328e-04, 6.72702794e-04,\n",
" 1.20889547e-03, 1.88107823e-03, 2.64380500e-03, 3.45477089e-03,\n",
" 4.28145425e-03],\n",
" [2.27434005e-09, 4.64820324e-08, 9.46792454e-07, 1.37633087e-05,\n",
" 7.66349258e-05, 2.27211058e-04, 4.84392425e-04, 8.45214177e-04,\n",
" 1.29153416e-03, 1.79815432e-03, 2.33954331e-03, 2.89372122e-03,\n",
" 3.44357151e-03],\n",
" [4.75539874e-09, 1.07745670e-07, 2.21974369e-06, 2.13823078e-05,\n",
" 8.28884513e-05, 2.02094612e-04, 3.83498729e-04, 6.23728731e-04,\n",
" 9.14110395e-04, 1.24216150e-03, 1.59279897e-03, 1.95027457e-03,\n",
" 2.30064266e-03]], dtype=float32),\n",
" 'vth': array([[0.4175789 , 0.4175789 , 0.4175789 , 0.4175789 , 0.4175789 ,\n",
" 0.4175789 , 0.4175789 , 0.4175789 , 0.4175789 , 0.4175789 ,\n",
" 0.4175789 , 0.4175789 , 0.4175789 ],\n",
" [0.31012315, 0.31012315, 0.31012315, 0.31012315, 0.31012315,\n",
" 0.31012315, 0.31012315, 0.31012315, 0.31012315, 0.31012315,\n",
" 0.31012315, 0.31012315, 0.31012315],\n",
" [0.25189924, 0.25189924, 0.25189924, 0.25189924, 0.25189924,\n",
" 0.25189924, 0.25189924, 0.25189924, 0.25189924, 0.25189924,\n",
" 0.25189924, 0.25189924, 0.25189924]], dtype=float32),\n",
" 'gm': array([[1.0355391e-08, 1.6762097e-07, 2.7663464e-06, 4.3945060e-05,\n",
" 4.0340933e-04, 1.3237044e-03, 2.8199644e-03, 4.5541674e-03,\n",
" 6.1132847e-03, 7.2515998e-03, 7.9314727e-03, 8.2337745e-03,\n",
" 8.2622319e-03],\n",
" [6.8324205e-08, 1.4071227e-06, 2.8058514e-05, 3.0460340e-04,\n",
" 1.0163147e-03, 2.0270473e-03, 3.1103410e-03, 4.0744538e-03,\n",
" 4.8087859e-03, 5.2804318e-03, 5.5105267e-03, 5.5445507e-03,\n",
" 5.4319035e-03],\n",
" [1.4800021e-07, 3.3604522e-06, 6.2207786e-05, 3.6775280e-04,\n",
" 8.8785321e-04, 1.5031686e-03, 2.1188618e-03, 2.6710527e-03,\n",
" 3.1156084e-03, 3.4199473e-03, 3.5661045e-03, 3.5598569e-03,\n",
" 3.4301258e-03]], dtype=float32),\n",
" 'gds': array([[4.2991338e-10, 7.1589881e-09, 1.2225600e-07, 2.0337011e-06,\n",
" 2.1824198e-05, 7.4040574e-05, 1.4492353e-04, 2.4114188e-04,\n",
" 3.7066598e-04, 5.5198371e-04, 8.0706168e-04, 1.1493404e-03,\n",
" 1.5826311e-03],\n",
" [2.1960556e-09, 4.7003134e-08, 9.8667385e-07, 1.2744471e-05,\n",
" 4.5941855e-05, 9.0867376e-05, 1.5159344e-04, 2.3945510e-04,\n",
" 3.7418643e-04, 5.7869806e-04, 8.6932670e-04, 1.2510468e-03,\n",
" 1.7183991e-03],\n",
" [3.8504893e-09, 9.1464564e-08, 1.8656532e-06, 1.3157433e-05,\n",
" 3.2548171e-05, 5.7141449e-05, 8.8717396e-05, 1.3273282e-04,\n",
" 2.0318832e-04, 3.2743323e-04, 5.4010993e-04, 8.6352450e-04,\n",
" 1.2929583e-03]], dtype=float32),\n",
" 'length': array([[1.3e-07, 1.3e-07, 1.3e-07, 1.3e-07, 1.3e-07, 1.3e-07, 1.3e-07,\n",
" 1.3e-07, 1.3e-07, 1.3e-07, 1.3e-07, 1.3e-07, 1.3e-07],\n",
" [2.6e-07, 2.6e-07, 2.6e-07, 2.6e-07, 2.6e-07, 2.6e-07, 2.6e-07,\n",
" 2.6e-07, 2.6e-07, 2.6e-07, 2.6e-07, 2.6e-07, 2.6e-07],\n",
" [5.2e-07, 5.2e-07, 5.2e-07, 5.2e-07, 5.2e-07, 5.2e-07, 5.2e-07,\n",
" 5.2e-07, 5.2e-07, 5.2e-07, 5.2e-07, 5.2e-07, 5.2e-07]]),\n",
" 'vbs': 0.0,\n",
" 'vgs': array([[0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. , 1.1, 1.2],\n",
" [0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. , 1.1, 1.2],\n",
" [0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. , 1.1, 1.2]]),\n",
" 'vds': 0.6}"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"nmos.extracted_table\n"
]
},
{
"cell_type": "markdown",
"id": "2fc675aa-6d59-4d74-83e2-18c56353db0d",
"metadata": {},
"source": [
"# NMOS GMID"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "b7cc630f-b385-47a6-a6f9-ac0d10effffe",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "6135647378824854a249008cff2939aa",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.13 μm', '…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"width_values = nmos.width\n",
"id_values = nmos.extracted_table['id']\n",
"gm_values = nmos.extracted_table['gm']\n",
"gds_values = nmos.extracted_table['gds']\n",
"vgs_values= nmos.extracted_table['vgs']\n",
"\n",
"plot_data_vs_data(\n",
" gm_values/id_values,\n",
" gm_values/gds_values,\n",
" vgs_values,\n",
" length_2d_nmos,\n",
" 'gm/id',\n",
" 'gm/gds'\n",
")"
]
},
{
"cell_type": "markdown",
"id": "e847c359-b57e-4e84-b0dc-93616d575efd",
"metadata": {},
"source": [
"# PMOS GMID"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "3727c42d-a4bf-4eb0-bc11-6e859ae41324",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "3de96606da1946dcb4e15f6b7e281eec",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Dropdown(description='Length:', layout=Layout(width='500px'), options=('Show All', '0.13 μm', '…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"width_values = pmos.width\n",
"id_values = pmos.extracted_table['id']\n",
"gm_values = pmos.extracted_table['gm']\n",
"gds_values = pmos.extracted_table['gds']\n",
"vgs_values= pmos.extracted_table['vgs']\n",
"\n",
"plot_data_vs_data(gm_values/id_values, gm_values/gds_values, vgs_values, length_2d_pmos, 'gm/id', 'gm/gds')"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}