IHP-AnalogAcademy/modules/module_1_bandgap_reference/scripting/OTA_low_gain.ipynb

852 lines
46 KiB
Plaintext
Raw Normal View History

2024-11-12 09:46:24 +01:00
{
"cells": [
{
"cell_type": "code",
"execution_count": 429,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"#for windows\n",
"import sys\n",
"sys.path.append(r'C:\\Users\\pedersen\\gmid') # path to gmid repository\n",
"# ------\n",
"import matplotlib.pyplot as plt\n",
"from mosplot import load_lookup_table, LoadMosfet # make sure that mosplot can be found in the python path\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": 430,
"metadata": {},
"outputs": [],
"source": [
"pmos_lv_path = r'C:\\Users\\pedersen\\Desktop\\OpenDesingCourse\\no_touch_files\\gmoveridpypmos_gmid_final.npy'\n",
"nmos_lv_path =r'C:\\Users\\pedersen\\Desktop\\OpenDesingCourse\\no_touch_files\\gmoveridpynmos_final_gmid.npy'\n",
"\n",
"lookup_table_pmos = load_lookup_table(pmos_lv_path)\n",
"lookup_table_nmos = load_lookup_table(nmos_lv_path)"
]
},
{
"cell_type": "code",
"execution_count": 431,
"metadata": {},
"outputs": [],
"source": [
"nmos = LoadMosfet(lookup_table=lookup_table_nmos, mos=\"nmos\", vsb=0.0, vds=0.6)\n",
"pmos = LoadMosfet(lookup_table=lookup_table_pmos, mos=\"pmos\", vsb=0, vds=-0.6, vgs=(-1.2, -0.1))"
]
},
{
"cell_type": "code",
"execution_count": 432,
"metadata": {},
"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",
" length_flat = np.array(length).flatten()\n",
"\n",
" # Ensure all inputs have the same length\n",
" if not (len(x_values_flat) == len(y_values_flat) == len(z_values_flat) == len(length_flat)):\n",
" raise ValueError(\"All input arrays (x_values, y_values, z_values, length) must have the same number of elements.\")\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=(8, 6)) # Ensure the plot is drawn fresh for each update\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.1\n",
" # Recalculate the mask with matching shapes\n",
" mask = np.abs(length_flat * 1e6 - selected_length_in_micro) < tolerance\n",
"\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='Select Length:',\n",
" )\n",
"\n",
" x_value_widget = widgets.FloatText(\n",
" value=np.mean(x_values_flat),\n",
" description=f\"Select {x_axis_name}:\",\n",
" disabled=False\n",
" )\n",
"\n",
" y_value_widget = widgets.FloatText(\n",
" value=None,\n",
" description=f\"Set {y_axis_name}:\",\n",
" disabled=True\n",
" )\n",
"\n",
" z_value_widget = widgets.FloatText(\n",
" value=None,\n",
" description=f\"Corresponding z value:\",\n",
" disabled=True\n",
" )\n",
"\n",
" select_x_or_y_widget = widgets.Checkbox(\n",
" value=True,\n",
" description=f\"Select {x_axis_name} (uncheck for {y_axis_name})\",\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",
"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"
]
},
{
"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": 434,
"metadata": {},
"outputs": [],
"source": [
"vdd = 1.2 # supply voltage\n",
"Cl = 500e-15 # load capacitance\n",
"Av_wish = 80 # in db\n",
"phase_margin = 65 # At least :)\n",
"fdominant = 1000 # dominant pole frequency\n",
"CC = 0.3*Cl \n",
"GBW = fdominant * dB_to_linear(Av_wish) # Gain Bandwidth Product"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# First desigining the output stage looking at the output transistor"
]
},
{
"cell_type": "code",
"execution_count": 435,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "7885bed517d544a6a5186b585b73be1c",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"nmos_M6 = LoadMosfet(lookup_table=lookup_table_nmos, mos=\"nmos\", vsb=0, vds=0.6)\n",
"width_values_M6 = nmos_M6.extracted_table['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, nmos_M6.extracted_table['lengths'], 'gm/id', 'gds')"
]
},
{
"cell_type": "code",
"execution_count": 436,
"metadata": {},
"outputs": [],
"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",
"gm_M6 = 2*np.pi*GBW*Cl # we have se\n",
"id_outputstage = gm_M6/gmid_M6\n",
"gmro_M6 = 55.68\n",
"ro_M6 = gmro_M6/gm_M6\n"
]
},
{
"cell_type": "code",
"execution_count": 437,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"gmro_M7 = 334.08\n"
]
}
],
"source": [
"# Now for the pmos output transistor we must have 3 time the output impedance to insure max output gain\n",
"ro_M7 = 3*ro_M6\n",
"\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": 438,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "36da585aea1b4f0bb5722f07a41fc809",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"pmos_M7 = LoadMosfet(lookup_table=lookup_table_pmos, mos=\"pmos\", vsb=0, vds=-0.6, vgs=(-1.2, -0.1))\n",
"width_values_M7 = pmos_M7.extracted_table['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, pmos_M7.extracted_table['lengths'], 'gm/id', 'gds')\n"
]
},
{
"cell_type": "code",
"execution_count": 439,
"metadata": {},
"outputs": [],
"source": [
"# For the criteria to be met we must have \n",
"L_M7 = 3.38e-6"
]
},
{
"cell_type": "code",
"execution_count": 440,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"L_M6 = 9.75e-06\n",
"gmid_M6 = 5.00\n",
"L_M7 = 3.38e-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": 441,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "b0beb804bcf4452593b1eb60e6baed9d",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "0fc744fa24c34ef7a0d5b5590746f95c",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…"
]
},
"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, nmos_M6.extracted_table['lengths'], '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, pmos_M7.extracted_table['lengths'], 'gm/id', 'M7 id/W', log=True)"
]
},
{
"cell_type": "code",
"execution_count": 442,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Output Stage Amplification Summary\n",
"Width and Lengths for M6\n",
" W = 2.28 um\n",
" L = 9.75 um\n",
"Inversion Region for M6: Strong Inversion\n",
"\n",
"Width and Lengths for M7\n",
" W = 14.61 um\n",
" L = 3.38 um\n",
"Inversion Region for M7: Strong Inversion\n",
"\n",
"Output Stage Bias:\n",
" Output Current : 6.28 μA\n",
"\n",
"Output Stage Gain:\n",
" Av2 = 41.76 (32.42 dB)\n",
"\n"
]
}
],
"source": [
"id_over_W_M6 = 2.76\n",
"id_over_W_M7 = 0.43\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": 443,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1.392e-06"
]
},
"execution_count": 443,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Now we assume that the max gmro for our nmos in the input stage is 55.68\n",
"gmro_M34_assumption = 55.68\n",
"gmid_M34 = 5\n",
"Rout1 = 8e6\n",
"gm_M34 = gmro_M34_assumption/Rout1\n",
"id_outputstage = gm_M34/gmid_M34\n",
"id_outputstage"
]
},
{
"cell_type": "code",
"execution_count": 444,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"gmro_M12 = 718.39\n",
"gmid_M12 = 21.50\n"
]
}
],
"source": [
"gm_M12 = dB_to_linear(float(Av_wish-Av2_db))/Rout1\n",
"CC = 1/(2*np.pi*fdominant* Rout1 * (1 + Av2))\n",
"gmro_M12 = gm_M12 * Rout1*3\n",
"gmid_M12 = gm_M12/id_outputstage\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": 445,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "1a84ed5efc7d45a490772af3f76341ee",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "1909ac8e35914a33aee50aedb2a86205",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"pmos_M12 = LoadMosfet(lookup_table=lookup_table_pmos, mos=\"pmos\", vsb=0, vds=-0.6, vgs=(-1.2, -0.1))\n",
"nmos_M34 = LoadMosfet(lookup_table=lookup_table_nmos, mos=\"nmos\", vsb=0, vds=0.6)\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, pmos_M12.extracted_table['lengths'], 'gm/id', 'm12 gm/gds', log=True)\n",
"plot_data_vs_data(gm_values_M34/id_values_M34, gm_values_M34/gds_values_M34, vgs_values_M34, nmos_M34.extracted_table['lengths'], 'gm/id', 'm34 gm/gds', log=True)"
]
},
{
"cell_type": "code",
"execution_count": 446,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"L_M12 = 3.38e-06\n",
"gmid_M12 = 21.50\n",
"L_M34 = 9.75e-06\n",
"gmid_M34 = 5.00\n"
]
}
],
"source": [
"L_M12 = 3.38e-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": 447,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "6907db96f53d4e378c57942f9be804a3",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "b9ee6f9f9f54489796067b7b5209ab57",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Dropdown(description='Select Length:', options=('Show All', '0.13 μm', '0.26 μm', '0.39 μm', '0…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"width_values_M12 = pmos_M12.extracted_table['width']\n",
"width_values_M34 = nmos_M34.extracted_table['width']\n",
"\n",
"plot_data_vs_data(gm_values_M12/id_values_M12,id_values_M12/width_values_M12, vgs_values_M12, pmos_M12.extracted_table['lengths'], '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, nmos_M34.extracted_table['lengths'], 'gm/id', 'M34 id/W', log=True)\n"
]
},
{
"cell_type": "code",
"execution_count": 448,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Input Stage Amplification Summary\n",
"Width and Lengths for M12\n",
" W = 34.80 um\n",
" L = 3.38 um\n",
"Inversion Region for M12: Weak Inversion\n",
"Width and Lengths for M34\n",
" W = 0.50 um\n",
" L = 9.75 um\n",
"Inversion Region for M34: Strong Inversion\n",
"Input Stage Bias:\n",
" Output Current : 1.39 μA\n",
" Output tail current: 2.78 μA\n",
"\n"
]
}
],
"source": [
"id_over_W_M12 = 0.04\n",
"id_over_W_M34 = 2.76\n",
"\n",
"display_outcurrent, unit_outcurrent = display_current(id_outputstage)\n",
"\n",
"W_M12 = id_outputstage/id_over_W_M12\n",
"W_M34 = id_outputstage/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",
" Output Current : {display_outcurrent:.2f} {unit_outcurrent}\n",
" Output tail current: {display_outcurrent*2:.2f} {unit_outcurrent}\n",
"\"\"\"\n",
"print(input_stage_summary)"
]
},
{
"cell_type": "code",
"execution_count": 451,
"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 = 2.28 um\n",
" L = 9.75 um\n",
"Inversion Region for M6: Strong Inversion\n",
"\n",
"Width and Lengths for M7\n",
" W = 14.61 um\n",
" L = 3.38 um\n",
"Inversion Region for M7: Strong Inversion\n",
"\n",
"Output Stage Bias:\n",
" Output Current : 6.28 μA\n",
"\n",
"Output Stage Gain:\n",
" Av2 = 41.76 (32.42 dB)\n",
"\n",
"Input Stage:\n",
" \n",
"Input Stage Amplification Summary\n",
"Width and Lengths for M12\n",
" W = 34.80 um\n",
" L = 3.38 um\n",
"Inversion Region for M12: Weak Inversion\n",
"Width and Lengths for M34\n",
" W = 0.50 um\n",
" L = 9.75 um\n",
"Inversion Region for M34: Strong Inversion\n",
"Input Stage Bias:\n",
" Output Current : 1.39 μA\n",
" Output tail current: 2.78 μA\n",
"\n",
"\n",
"Gain and frequency response:\n",
" Gain Bandwidth Product: 10000000.0 Hz\n",
" Dominant Pole Frequency: 1000 Hz\n",
" Output Stage Compensation Capacitance: 4.652564987485247e-13 F\n",
" Load Capacitance: 5e-13 F\n",
" dominant pole: 1000.0 Hz\n",
" non dominant pole: 10000000.000000002 Hz\n",
"\n",
"\n"
]
}
],
"source": [
"# summarizing everything\\\n",
"gmro_M12_actual = 800\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",
"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",
"\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} Hz\n",
"\n",
"\"\"\"\n",
"print(summary)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.12.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
}