From a45556d2c0c510c8bef7b08717ff7e40a8f264f6 Mon Sep 17 00:00:00 2001 From: PhillipRambo Date: Mon, 2 Feb 2026 13:02:48 +0100 Subject: [PATCH] fix redundancy and fix module 1 --- .../gmid_example/scripting/OTA_low_gain.ipynb | 949 ++++++++++++++++++ 1 file changed, 949 insertions(+) create mode 100644 modules/module_1_bandgap_reference/part_1_OTA/gmid_example/scripting/OTA_low_gain.ipynb diff --git a/modules/module_1_bandgap_reference/part_1_OTA/gmid_example/scripting/OTA_low_gain.ipynb b/modules/module_1_bandgap_reference/part_1_OTA/gmid_example/scripting/OTA_low_gain.ipynb new file mode 100644 index 00000000..ba3abb55 --- /dev/null +++ b/modules/module_1_bandgap_reference/part_1_OTA/gmid_example/scripting/OTA_low_gain.ipynb @@ -0,0 +1,949 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "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": 2, + "metadata": {}, + "outputs": [], + "source": [ + "lookup_table_nmos = load_lookup_table('../../../module_0_foundations/sg13_nmos_lv_LUT.npz')\n", + "lookup_table_pmos = load_lookup_table('../../../module_0_foundations/sg13_pmos_lv_LUT.npz')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_keys(['sg13_lv_nmos ', 'description', 'simulator', 'parameter_names', 'device_parameters'])\n" + ] + } + ], + "source": [ + "print(lookup_table_nmos.keys())" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "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": 5, + "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": 6, + "metadata": {}, + "outputs": [], + "source": [ + "### ------ automated flattening based on shape ------### \n", + "reshaped_lengths_pmos = tile_length_to_match_data(pmos.length, pmos.extracted_table['gm'])\n", + "reshaped_lengths_nmos = tile_length_to_match_data(nmos.length, nmos.extracted_table['gm'])" + ] + }, + { + "attachments": { + "image.png": { + "image/png": "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" + } + }, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![image.png](attachment:image.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# As Starting point the input stage is sized." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "vdd = 1.2 # supply voltage\n", + "Cl = 500e-15 # load capacitance\n", + "Av_wish = 75 # in db approximatly 70-75db\n", + "fdominant = 1000 # dominant pole frequency\n", + "CC = Cl*1.5\n", + "GBW = fdominant * dB_to_linear(Av_wish) # Gain Bandwidth Product" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "gm_M12 = GBW * CC * 2 * np.pi\n", + "gm_M6 = 15 * gm_M12" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "72114aa1708645529ec782f5479a3a60", + "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": [ + "nmos_M6 = Mosfet(lookup_table=lookup_table_nmos, mos=\"sg13_lv_nmos \", vbs=0, vds=0.6)\n", + "reshaped_lengths_nmos_M6 = tile_length_to_match_data(nmos_M6.length, nmos_M6.extracted_table['gm'])\n", + "\n", + "width_values_M6 = nmos_M6.width\n", + "id_values_M6 = nmos_M6.extracted_table['id']\n", + "gm_values_M6 = nmos_M6.extracted_table['gm']\n", + "gds_values_M6 = nmos_M6.extracted_table['gds']\n", + "vgs_values_M6 = nmos_M6.extracted_table['vgs']\n", + "\n", + "plot_data_vs_data(gm_values_M6/id_values_M6, gm_values_M6/gds_values_M6, vgs_values_M6, reshaped_lengths_nmos_M6, 'gm/id', 'gm/gds')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "7.949913192125772e-05" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# For max gmro and vgs of 0.6V we have gmid of 5\n", + "L_M6 = 9.75e-6\n", + "gmid_M6 = 5 # strong inversion, i.e saturation\n", + "id_outputstage = gm_M6/gmid_M6\n", + "gmro_M6 = 55.68\n", + "ro_M6 = gmro_M6/gm_M6\n", + "id_outputstage" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "gmro_M7 = 222.72\n" + ] + } + ], + "source": [ + "# Now for the pmos output transistor we must have 3 time the output impedance to insure max output gain\n", + "ro_M7 = 2 * ro_M6\n", + "# we want to place this transistor in moderate inversion\n", + "gmid_M7 = 10\n", + "gm_M7 = gmid_M7 * id_outputstage\n", + "gmro_M7 = gm_M7 * ro_M7\n", + "print(f'gmro_M7 = {gmro_M7:.2f}')" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d2c6f85fa97b4d9d8844e236d1b23f3f", + "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": [ + "pmos_M7 = Mosfet(lookup_table=lookup_table_pmos, mos=\"sg13_lv_pmos\", vbs=0, vds=-0.6, vgs=(-1.2, -0.1))\n", + "reshaped_lengths_pmos_M7 = tile_length_to_match_data(pmos_M7.length, pmos_M7.extracted_table['gm'])\n", + "\n", + "width_values_M7 = pmos_M7.width\n", + "id_values_M7 = pmos_M7.extracted_table['id']\n", + "gm_values_M7 = pmos_M7.extracted_table['gm']\n", + "gds_values_M7 = pmos_M7.extracted_table['gds']\n", + "vgs_values_M7 = pmos_M7.extracted_table['vgs']\n", + "\n", + "plot_data_vs_data(gm_values_M7/id_values_M7, gm_values_M7/gds_values_M7, vgs_values_M7, reshaped_lengths_pmos_M7, 'gm/id', 'gds')\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "# For the criteria to be met we must have \n", + "L_M7 = 2.08e-6\n", + "gmro_M7 = 390" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "L_M6 = 9.75e-06\n", + "gmid_M6 = 5.00\n", + "L_M7 = 2.08e-06\n", + "gmid_M7 = 10.00\n" + ] + } + ], + "source": [ + "print(f'L_M6 = {L_M6:.2e}')\n", + "print(f'gmid_M6 = {gmid_M6:.2f}')\n", + "print(f'L_M7 = {L_M7:.2e}')\n", + "print(f'gmid_M7 = {gmid_M7:.2f}')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3423adf060a5431c9ed9dcfd25cdd5cb", + "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" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d9a07198c63c44209996a193ba18660b", + "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": [ + "plot_data_vs_data(gm_values_M6/id_values_M6,id_values_M6/width_values_M6, vgs_values_M6, reshaped_lengths_nmos_M6, 'gm/id', 'M6 id/W', log=True)\n", + "plot_data_vs_data(gm_values_M7/id_values_M7, id_values_M7/width_values_M7,vgs_values_M7, reshaped_lengths_pmos_M7, 'gm/id', 'M7 id/W', log=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Output Stage Amplification Summary\n", + "Width and Lengths for M6\n", + " W = 28.80 um\n", + " L = 9.75 um\n", + "Inversion Region for M6: Strong Inversion\n", + "\n", + "Width and Lengths for M7\n", + " W = 75.00 um\n", + " L = 2.08 um\n", + "Inversion Region for M7: Strong Inversion\n", + "\n", + "Output Stage Bias:\n", + " Output Current : 79.50 μA\n", + "\n", + "Output Stage Gain:\n", + " Av2 = 37.12 (31.39 dB)\n", + "\n" + ] + } + ], + "source": [ + "id_over_W_M6 = 2.76\n", + "id_over_W_M7 = 1.06\n", + "\n", + "display_outcurrent, unit_outcurrent = display_current(id_outputstage)\n", + "Av2 = gm_M6 * (ro_M6 * ro_M7)/(ro_M6 + ro_M7)\n", + "Av2_db = 20 * np.log10(Av2)\n", + "\n", + "W_M6 = id_outputstage/id_over_W_M6\n", + "W_M7 = id_outputstage/id_over_W_M7\n", + "\n", + "output_stage_summary = f\"\"\"\n", + "Output Stage Amplification Summary\n", + "Width and Lengths for M6\n", + " W = {W_M6*1e6:.2f} um\n", + " L = {L_M6*1e6:.2f} um\n", + "Inversion Region for M6: {determine_inversion_region(gmid_M6, 'nmos')}\n", + "\n", + "Width and Lengths for M7\n", + " W = {W_M7*1e6:.2f} um\n", + " L = {L_M7*1e6:.2f} um\n", + "Inversion Region for M7: {determine_inversion_region(gmid_M7, 'pmos')}\n", + "\n", + "Output Stage Bias:\n", + " Output Current : {display_outcurrent:.2f} {unit_outcurrent}\n", + "\n", + "Output Stage Gain:\n", + " Av2 = {Av2:.2f} ({Av2_db:.2f} dB)\n", + "\"\"\"\n", + "\n", + "print(output_stage_summary)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# For sizing the output stage we must insure that we hit our target for the first pole, and get max gain" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2.0004294796936965e-06" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Now we assume that the max gmro for our nmos in the input stage is 55.68\n", + "fp2 = gm_M6/(2*np.pi*Cl)\n", + "gmro_M34_assumption = 55.68\n", + "gmid_M34 = 5\n", + "Rout1 = 1/(2*np.pi*fdominant* CC * (1 + Av2))\n", + "gm_M34 = gmro_M34_assumption/Rout1\n", + "id_branch = gm_M34/gmid_M34\n", + "id_branch" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mirror_pole_assumption = 20.95 MHz\n", + "fp2 = 126.52679816782855 MHz\n" + ] + } + ], + "source": [ + "mirror_pole_assumption = (gm_M34/( 4*np.pi*38e-15))*1e-6\n", + "print(f'mirror_pole_assumption = {mirror_pole_assumption:.2f} MHz')\n", + "print(f'fp2 = {fp2*1e-6:} MHz')" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "gmro_M12 = 442.56\n", + "gmid_M12 = 13.25\n" + ] + } + ], + "source": [ + "gmro_M12 = gm_M12 * Rout1*3\n", + "gmid_M12 = gm_M12/id_branch\n", + "print(f'gmro_M12 = {gmro_M12:.2f}')\n", + "print(f'gmid_M12 = {gmid_M12:.2f}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Finding the dimensions for the input stage transistors" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "bc344d417b284a71abea7fb90081872e", + "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" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b312811f418442868110cc154d1cd4b7", + "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": [ + "pmos_M12 = Mosfet(lookup_table=lookup_table_pmos, mos=\"sg13_lv_pmos\", vbs=-0.2, vds=-0.6, vgs=(-1.2, -0.1))\n", + "nmos_M34 = Mosfet(lookup_table=lookup_table_nmos, mos=\"sg13_lv_nmos \", vbs=0, vds=0.6)\n", + "reshaped_lengths_nmos_M34 = tile_length_to_match_data(nmos_M34.length, nmos_M34.extracted_table['gm'])\n", + "reshaped_lengths_pmos_M12 = tile_length_to_match_data(pmos_M12.length, pmos_M12.extracted_table['gm'])\n", + "\n", + "id_values_M12 = pmos_M12.extracted_table['id']\n", + "gm_values_M12 = pmos_M12.extracted_table['gm']\n", + "gds_values_M12 = pmos_M12.extracted_table['gds']\n", + "vgs_values_M12 = pmos_M12.extracted_table['vgs']\n", + "\n", + "id_values_M34 = nmos_M34.extracted_table['id']\n", + "gm_values_M34 = nmos_M34.extracted_table['gm']\n", + "gds_values_M34 = nmos_M34.extracted_table['gds']\n", + "vgs_values_M34 = nmos_M34.extracted_table['vgs']\n", + "\n", + "plot_data_vs_data(gm_values_M12/id_values_M12, gm_values_M12/gds_values_M12, vgs_values_M12, reshaped_lengths_pmos_M12, 'gm/id', 'm12 gm/gds')\n", + "plot_data_vs_data(gm_values_M34/id_values_M34, gm_values_M34/gds_values_M34, vgs_values_M34, reshaped_lengths_nmos_M34, 'gm/id', 'm34 gm/gds')" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "L_M12 = 3.64e-06\n", + "gmid_M12 = 13.25\n", + "L_M34 = 9.75e-06\n", + "gmid_M34 = 5.00\n" + ] + } + ], + "source": [ + "L_M12 = 3.64e-6\n", + "L_M34 = 9.75e-6\n", + "\n", + "print(f'L_M12 = {L_M12:.2e}')\n", + "print(f'gmid_M12 = {gmid_M12:.2f}')\n", + "print(f'L_M34 = {L_M34:.2e}')\n", + "print(f'gmid_M34 = {gmid_M34:.2f}')\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "e503a959702844d09e52f5cc34072b5b", + "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" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9299f09d02b1408b88c4ad86a163ce34", + "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_M12 = pmos_M12.width\n", + "width_values_M34 = nmos_M34.width\n", + "\n", + "plot_data_vs_data(gm_values_M12/id_values_M12,id_values_M12/width_values_M12, vgs_values_M12, reshaped_lengths_pmos_M12, 'gm/id', 'M12 id/W', log=True)\n", + "plot_data_vs_data(gm_values_M34/id_values_M34, id_values_M34/width_values_M34,vgs_values_M34, reshaped_lengths_nmos_M34, 'gm/id', 'M34 id/W', log=True)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Input Stage Amplification Summary\n", + "Width and Lengths for M12\n", + " W = 7.41 um\n", + " L = 3.64 um\n", + "Inversion Region for M12: Moderate Inversion\n", + "Width and Lengths for M34\n", + " W = 0.72 um\n", + " L = 9.75 um\n", + "Inversion Region for M34: Strong Inversion\n", + "Input Stage Bias:\n", + " Branch Current : 2.00 μA\n", + " input Tail current: 4.00 μA\n", + "\n" + ] + } + ], + "source": [ + "id_over_W_M12 = 0.27\n", + "id_over_W_M34 = 2.76\n", + "\n", + "display_id_branch, unit_id_branch = display_current(id_branch)\n", + "\n", + "W_M12 = id_branch/id_over_W_M12\n", + "W_M34 = id_branch/id_over_W_M34\n", + "\n", + "\n", + "input_stage_summary = f\"\"\"\n", + "Input Stage Amplification Summary\n", + "Width and Lengths for M12\n", + " W = {W_M12*1e6:.2f} um\n", + " L = {L_M12*1e6:.2f} um\n", + "Inversion Region for M12: {determine_inversion_region(gmid_M12, 'pmos')}\n", + "Width and Lengths for M34\n", + " W = {W_M34*1e6:.2f} um\n", + " L = {L_M34*1e6:.2f} um\n", + "Inversion Region for M34: {determine_inversion_region(gmid_M34, 'nmos')}\n", + "Input Stage Bias:\n", + " Branch Current : {display_id_branch:.2f} {unit_id_branch}\n", + " input Tail current: {display_id_branch*2:.2f} {unit_id_branch}\n", + "\"\"\"\n", + "print(input_stage_summary)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Amplifier is stable\n", + "\n", + "Summary\n", + "Output Stage:\n", + " \n", + "Output Stage Amplification Summary\n", + "Width and Lengths for M6\n", + " W = 28.80 um\n", + " L = 9.75 um\n", + "Inversion Region for M6: Strong Inversion\n", + "\n", + "Width and Lengths for M7\n", + " W = 75.00 um\n", + " L = 2.08 um\n", + "Inversion Region for M7: Strong Inversion\n", + "\n", + "Output Stage Bias:\n", + " Output Current : 79.50 μA\n", + "\n", + "Output Stage Gain:\n", + " Av2 = 37.12 (31.39 dB)\n", + "\n", + "Input Stage:\n", + " \n", + "Input Stage Amplification Summary\n", + "Width and Lengths for M12\n", + " W = 7.41 um\n", + " L = 3.64 um\n", + "Inversion Region for M12: Moderate Inversion\n", + "Width and Lengths for M34\n", + " W = 0.72 um\n", + " L = 9.75 um\n", + "Inversion Region for M34: Strong Inversion\n", + "Input Stage Bias:\n", + " Branch Current : 2.00 μA\n", + " input Tail current: 4.00 μA\n", + "\n", + " Input stage gain: 107.48 (40.63 dB)\n", + "\n", + "Gain and frequency response:\n", + " Gain Bandwidth Product: 5623413.251903491 Hz\n", + " Dominant Pole Frequency: 1000 Hz\n", + " Output Stage Compensation Capacitance: 7.5e-13 F\n", + " Load Capacitance: 5e-13 F\n", + " dominant pole: 1000.0 Hz\n", + " non dominant pole: 126.52679816782855 MHz\n", + " mirror pole assumption: 20.945936842105265 MHz\n", + " Total gain: 3989.66 (72.02 dB)\n", + "\n", + "\n", + "\n" + ] + } + ], + "source": [ + "# summarizing everything\\\n", + "gmro_M12_actual = 396\n", + "ro_M12 = gmro_M12_actual/gm_M12\n", + "ro_M34 = gmro_M34_assumption/gm_M34\n", + "Av1 = gm_M12 * (ro_M12 * ro_M34)/(ro_M12 + ro_M34)\n", + "f_pole_1 = 1/(2*np.pi * Rout1 * CC * (1 + Av2))\n", + "f_pole_2 = gm_M6/(2*np.pi * Cl)\n", + "\n", + "\n", + "decades = (20*np.log10(Av1) + Av2_db) / 20\n", + "\n", + "if f_pole_1 * 10**decades < f_pole_2:\n", + " print(\"Amplifier is stable\")\n", + "else:\n", + " print(\"Amplifier is not stable\")\n", + "\n", + "\n", + "\n", + "summary = f\"\"\"\n", + "Summary\n", + "Output Stage:\n", + " {output_stage_summary}\n", + "Input Stage:\n", + " {input_stage_summary}\n", + " Input stage gain: {gm_M12 * (ro_M12 * ro_M34)/(ro_M12 + ro_M34):.2f} ({20*np.log10(gm_M12 * (ro_M12 * ro_M34)/(ro_M12 + ro_M34)):.2f} dB)\n", + "\n", + "Gain and frequency response:\n", + " Gain Bandwidth Product: {GBW:} Hz\n", + " Dominant Pole Frequency: {fdominant} Hz\n", + " Output Stage Compensation Capacitance: {CC:} F\n", + " Load Capacitance: {Cl} F\n", + " dominant pole: {f_pole_1} Hz\n", + " non dominant pole: {f_pole_2*1e-6} MHz\n", + " mirror pole assumption: {mirror_pole_assumption} MHz\n", + " Total gain: {Av1*Av2:.2f} ({20*np.log10(Av1*Av2):.2f} dB)\n", + "\n", + "\n", + "\"\"\"\n", + "print(summary)" + ] + } + ], + "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": 4 +}