From 74da3405603c8e73b8ac292c2b4272b5069c3716 Mon Sep 17 00:00:00 2001 From: chetanyagoyal Date: Tue, 22 Oct 2024 17:43:58 +0000 Subject: [PATCH 1/4] fix imports in sky130_nist_tapeout for RL optimizer --- openfasoc/MLoptimization/quickstart.bash | 60 ++++++++++--------- openfasoc/MLoptimization/requirements.txt | 1 + .../tapeout_and_RL/sky130_nist_tapeout.py | 10 +++- 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/openfasoc/MLoptimization/quickstart.bash b/openfasoc/MLoptimization/quickstart.bash index 0344250f7..8845be6a3 100644 --- a/openfasoc/MLoptimization/quickstart.bash +++ b/openfasoc/MLoptimization/quickstart.bash @@ -22,14 +22,16 @@ else fi # ===================================================================== -# check that ngspice 40 is installed +# check that ngspice>40 is installed ngspice --version > test_ngspice_version.txt version_line=$(sed -n '2p' test_ngspice_version.txt) -expected_version="ngspice-40" -if [[ $version_line == *"$expected_version"* ]]; then - echo "Correct ngspice version ($expected_version) is installed." +version_number=$(echo "$version_line" | grep -oP '(?<=ngspice-)\d+') +required_version=40 + +if [[ "$version_number" -ge "$required_version" ]]; then + echo "Correct ngspice version ($version_line) is installed." else - echo "Error: Incorrect ngspice version. Expected $expected_version but found:" + echo "Error: Incorrect ngspice version. Expected version >= $required_version but found:" echo "$version_line" exit 1 fi @@ -45,30 +47,30 @@ is_installed() { } # Read the dependencies from requirements.txt and process each line -while IFS= read -r package || [ -n "$package" ]; do - # Remove leading/trailing whitespace - package=$(echo "$package" | xargs) - # Skip empty lines and comments - if [[ -z "$package" || "$package" == \#* ]]; then - continue - fi - # Extract the package name without extras and version specifiers for checking - package_name=$(echo "$package" | sed 's/\[.*\]//;s/[<>=].*//') - echo "Checking if $package is installed..." - if is_installed "$package_name"; then - echo "$package is already installed." - else - echo "$package is not installed. Installing..." - $PY_RUN -m pip install "$package" - # Check if the installation was successful - if is_installed "$package_name"; then - echo "$package installed successfully." - else - echo "Failed to install $package." - fi - fi - echo -done < "$requirements_file" +# while IFS= read -r package || [ -n "$package" ]; do +# # Remove leading/trailing whitespace +# package=$(echo "$package" | xargs) +# # Skip empty lines and comments +# if [[ -z "$package" || "$package" == \#* ]]; then +# continue +# fi +# # Extract the package name without extras and version specifiers for checking +# package_name=$(echo "$package" | sed 's/\[.*\]//;s/[<>=].*//') +# echo "Checking if $package is installed..." +# if is_installed "$package_name"; then +# echo "$package is already installed." +# else +# echo "$package is not installed. Installing..." +# $PY_RUN -m pip install "$package" +# # Check if the installation was successful +# if is_installed "$package_name"; then +# echo "$package installed successfully." +# else +# echo "Failed to install $package." +# fi +# fi +# echo +# done < "$requirements_file" echo "Dependency check and package installations complete." diff --git a/openfasoc/MLoptimization/requirements.txt b/openfasoc/MLoptimization/requirements.txt index a25b464d2..f4f38f5de 100644 --- a/openfasoc/MLoptimization/requirements.txt +++ b/openfasoc/MLoptimization/requirements.txt @@ -7,6 +7,7 @@ klayout safetensors requests tensorboard +numpy<2 ray==2.7.1 gym==0.26.2 gymnasium==0.28.1 diff --git a/openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130_nist_tapeout.py b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130_nist_tapeout.py index 0ecc9ea03..320f49141 100644 --- a/openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130_nist_tapeout.py +++ b/openfasoc/generators/glayout/tapeout/tapeout_and_RL/sky130_nist_tapeout.py @@ -1,16 +1,20 @@ import sys from os import path, rename, environ environ['OPENBLAS_NUM_THREADS'] = '1' +from pathlib import Path # path to glayout -sys.path.append(path.join(path.dirname(__file__), '../../')) - +sys.path.append(path.join(str(Path(__file__).resolve().parents[2]))) from gdsfactory.read.import_gds import import_gds from gdsfactory.components import text_freetype, rectangle from glayout.flow.pdk.util.comp_utils import prec_array, movey, align_comp_to_port, prec_ref_center from glayout.flow.pdk.util.port_utils import add_ports_perimeter, print_ports from gdsfactory.component import Component from glayout.flow.pdk.mappedpdk import MappedPDK -from glayout.flow.blocks.composite.opamp import opamp +try: + from glayout.flow.blocks.composite.opamp import opamp +except: + print('Regular opamp import failed! Using Fallback...') + from glayout.flow.blocks.opamp import opamp from glayout.flow.routing.L_route import L_route from glayout.flow.routing.straight_route import straight_route from glayout.flow.routing.c_route import c_route From dfe7a293b92ac6d7ba00f7383066c2f10d4bfdef Mon Sep 17 00:00:00 2001 From: chetanyagoyal Date: Tue, 22 Oct 2024 17:48:53 +0000 Subject: [PATCH 2/4] add utility function to test.py in MLopt --- openfasoc/MLoptimization/test.py | 106 ++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 17 deletions(-) diff --git a/openfasoc/MLoptimization/test.py b/openfasoc/MLoptimization/test.py index 1ec8e3dfb..350d85279 100644 --- a/openfasoc/MLoptimization/test.py +++ b/openfasoc/MLoptimization/test.py @@ -1,5 +1,6 @@ import numpy as np import glayout_import +import sys from sky130_nist_tapeout import safe_single_build_and_simulation from sky130_nist_tapeout import opamp_parameters_serializer @@ -7,25 +8,96 @@ import yaml from pathlib import Path import numpy as np +from typing import Union + +def get_indices_from_ranges(opamp_parameters: Union[dict, np.array]) -> np.array: + """The RL framework works by doing design searches within a user defined range for each parameter + For example, the RL framework may be told to search diffpair widths between 1um and 8um in steps of 1um + In this case, the RL framework will only ever try to make opamps with diff pair width one of [1,2,3,4,5,6,7,8] + this is done to limit the search space + The RL result will then show an index of the result. For example, if the best opamp in a design iteration had a with of 3um, + the corresponding index for 3um is 2 (3um is at the 2nd index in the array [1,2,3,4,5,6,7,8]) + + The purpose of this function to convert a list of opamp parameter into a list of indices into these range arrays + returns a list of indicies + + input can either be given in dictionary or np.array format + here is an example dictionary input: + {'half_diffpair_params': (-987.654321, -987.654321, -987.654321), 'diffpair_bias': (-987.654321, -987.654321, -987.654321), 'half_common_source_params': (-987.654321, -987.654321, -987.654321, -987.654321), 'half_common_source_bias': (-987.654321, -987.654321, -987.654321, -987.654321), 'output_stage_params': (-987.654321, -987.654321, -987), 'output_stage_bias': (-987, -987.654321, -987.654321), 'half_pload': (-987.654321, -987.654321, -987.654321), 'mim_cap_size': (-987.654321, -987.654321), 'mim_cap_rows': -987, 'rmult': -987} + + here is an example np.array indices output: + np.array([6, 2, 7, 6, 0, 5, 7, 0, 10, 6, 5, 13, 0, 6, 4, 2]) + """ + # convert to dictionary format first + if isinstance(opamp_parameters, dict): + opamp_parameter_dict = opamp_parameters + else: + opamp_parameter_dict = opamp_parameters_de_serializer(opamp_parameters) + # Function to find the closest index in the range + def closest_index(range_params, number): + start, end, step = range_params + values = np.arange(start, end, step) + idx = (np.abs(values - number)).argmin() + return idx + # loop through params and find closest indices + opamp_parameter_arr = list() + for key, val in params.items(): + if key == "diffpair_params0": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["half_diffpair_params"][0])) + elif key == "diffpair_params1": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["half_diffpair_params"][1])) + elif key == "diffpair_params2": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["half_diffpair_params"][2])) + elif key == "Diffpair_bias0": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["diffpair_bias"][0])) + elif key == "Diffpair_bias1": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["diffpair_bias"][1])) + elif key == "Diffpair_bias2": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["diffpair_bias"][2])) + elif key == "pamp_hparams0": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["half_common_source_params"][0])) + elif key == "pamp_hparams1": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["half_common_source_params"][1])) + elif key == "pamp_hparams2": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["half_common_source_params"][2])) + elif key == "bias0": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["half_common_source_bias"][0])) + elif key == "bias1": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["half_common_source_bias"][1])) + elif key == "bias2": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["half_common_source_bias"][2])) + elif key == "bias3": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["half_common_source_bias"][3])) + elif key == "half_pload1": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["half_pload"][0])) + elif key == "half_pload3": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["half_pload"][2])) + elif key == "mim_cap_rows": + opamp_parameter_arr.append(closest_index(val, opamp_parameter_dict["mim_cap_rows"])) + else: + raise ValueError("invalid key") + # this relies on the fact that the params dict should be in order + return np.array(opamp_parameter_arr) params = { - "diffpair_params0" : [1, 8, 1], - "diffpair_params1" : [0.5, 2.1, 0.1], - "diffpair_params2" : [1, 13, 1], - "Diffpair_bias0" : [1, 8, 1], - "Diffpair_bias1" : [1, 4.5, 0.5], - "Diffpair_bias2" : [3, 13, 1], - "pamp_hparams0" : [1, 9, 1], - "pamp_hparams1" : [0.5, 2.1, 0.1], - "pamp_hparams2" : [2, 14, 1], - "bias0" : [1, 8, 1], - "bias1" : [0.5, 2.1, 0.1], - "bias2" : [3, 18, 1], - "bias3" : [2, 4, 1], - "half_pload1": [3, 10, 1], - "half_pload3": [4, 9, 1], - "mim_cap_rows" : [1, 4, 1], - } + "diffpair_params0" : [1, 8, 1], + "diffpair_params1" : [0.5, 2.1, 0.1], + "diffpair_params2" : [1, 13, 1], + "Diffpair_bias0" : [1, 8, 1], + "Diffpair_bias1" : [1, 4.5, 0.5], + "Diffpair_bias2" : [3, 13, 1], + "pamp_hparams0" : [1, 9, 1], + "pamp_hparams1" : [0.5, 2.1, 0.1], + "pamp_hparams2" : [2, 14, 1], + "bias0" : [1, 8, 1], + "bias1" : [0.5, 2.1, 0.1], + "bias2" : [3, 18, 1], + "bias3" : [2, 4, 1], + "half_pload1": [3, 10, 1], + "half_pload3": [4, 9, 1], + "mim_cap_rows" : [1, 4, 1], + } + paramss = [] params_id = list(params.keys()) From 505aa682607670f0649af8bc895e5be5fe565d40 Mon Sep 17 00:00:00 2001 From: chetanyagoyal Date: Wed, 23 Oct 2024 03:17:22 +0000 Subject: [PATCH 3/4] remove comments and fix ngspice check version at 40 --- openfasoc/MLoptimization/quickstart.bash | 58 ++++++++++++------------ openfasoc/MLoptimization/test.py | 41 ++++++++--------- 2 files changed, 48 insertions(+), 51 deletions(-) diff --git a/openfasoc/MLoptimization/quickstart.bash b/openfasoc/MLoptimization/quickstart.bash index 8845be6a3..ffd6bb647 100644 --- a/openfasoc/MLoptimization/quickstart.bash +++ b/openfasoc/MLoptimization/quickstart.bash @@ -25,13 +25,11 @@ fi # check that ngspice>40 is installed ngspice --version > test_ngspice_version.txt version_line=$(sed -n '2p' test_ngspice_version.txt) -version_number=$(echo "$version_line" | grep -oP '(?<=ngspice-)\d+') -required_version=40 - -if [[ "$version_number" -ge "$required_version" ]]; then - echo "Correct ngspice version ($version_line) is installed." +expected_version="ngspice-40" +if [[ $version_line == *"$expected_version"* ]]; then + echo "Correct ngspice version ($expected_version) is installed." else - echo "Error: Incorrect ngspice version. Expected version >= $required_version but found:" + echo "Error: Incorrect ngspice version. Expected $expected_version but found:" echo "$version_line" exit 1 fi @@ -47,30 +45,30 @@ is_installed() { } # Read the dependencies from requirements.txt and process each line -# while IFS= read -r package || [ -n "$package" ]; do -# # Remove leading/trailing whitespace -# package=$(echo "$package" | xargs) -# # Skip empty lines and comments -# if [[ -z "$package" || "$package" == \#* ]]; then -# continue -# fi -# # Extract the package name without extras and version specifiers for checking -# package_name=$(echo "$package" | sed 's/\[.*\]//;s/[<>=].*//') -# echo "Checking if $package is installed..." -# if is_installed "$package_name"; then -# echo "$package is already installed." -# else -# echo "$package is not installed. Installing..." -# $PY_RUN -m pip install "$package" -# # Check if the installation was successful -# if is_installed "$package_name"; then -# echo "$package installed successfully." -# else -# echo "Failed to install $package." -# fi -# fi -# echo -# done < "$requirements_file" +while IFS= read -r package || [ -n "$package" ]; do + # Remove leading/trailing whitespace + package=$(echo "$package" | xargs) + # Skip empty lines and comments + if [[ -z "$package" || "$package" == \#* ]]; then + continue + fi + # Extract the package name without extras and version specifiers for checking + package_name=$(echo "$package" | sed 's/\[.*\]//;s/[<>=].*//') + echo "Checking if $package is installed..." + if is_installed "$package_name"; then + echo "$package is already installed." + else + echo "$package is not installed. Installing..." + $PY_RUN -m pip install "$package" + # Check if the installation was successful + if is_installed "$package_name"; then + echo "$package installed successfully." + else + echo "Failed to install $package." + fi + fi + echo +done < "$requirements_file" echo "Dependency check and package installations complete." diff --git a/openfasoc/MLoptimization/test.py b/openfasoc/MLoptimization/test.py index 350d85279..2826fc7b6 100644 --- a/openfasoc/MLoptimization/test.py +++ b/openfasoc/MLoptimization/test.py @@ -3,13 +3,32 @@ import sys from sky130_nist_tapeout import safe_single_build_and_simulation -from sky130_nist_tapeout import opamp_parameters_serializer +from sky130_nist_tapeout import opamp_parameters_serializer, opamp_parameters_de_serializer import yaml from pathlib import Path import numpy as np from typing import Union +params = { + "diffpair_params0" : [1, 8, 1], + "diffpair_params1" : [0.5, 2.1, 0.1], + "diffpair_params2" : [1, 13, 1], + "Diffpair_bias0" : [1, 8, 1], + "Diffpair_bias1" : [1, 4.5, 0.5], + "Diffpair_bias2" : [3, 13, 1], + "pamp_hparams0" : [1, 9, 1], + "pamp_hparams1" : [0.5, 2.1, 0.1], + "pamp_hparams2" : [2, 14, 1], + "bias0" : [1, 8, 1], + "bias1" : [0.5, 2.1, 0.1], + "bias2" : [3, 18, 1], + "bias3" : [2, 4, 1], + "half_pload1": [3, 10, 1], + "half_pload3": [4, 9, 1], + "mim_cap_rows" : [1, 4, 1], + } + def get_indices_from_ranges(opamp_parameters: Union[dict, np.array]) -> np.array: """The RL framework works by doing design searches within a user defined range for each parameter For example, the RL framework may be told to search diffpair widths between 1um and 8um in steps of 1um @@ -79,25 +98,6 @@ def closest_index(range_params, number): # this relies on the fact that the params dict should be in order return np.array(opamp_parameter_arr) -params = { - "diffpair_params0" : [1, 8, 1], - "diffpair_params1" : [0.5, 2.1, 0.1], - "diffpair_params2" : [1, 13, 1], - "Diffpair_bias0" : [1, 8, 1], - "Diffpair_bias1" : [1, 4.5, 0.5], - "Diffpair_bias2" : [3, 13, 1], - "pamp_hparams0" : [1, 9, 1], - "pamp_hparams1" : [0.5, 2.1, 0.1], - "pamp_hparams2" : [2, 14, 1], - "bias0" : [1, 8, 1], - "bias1" : [0.5, 2.1, 0.1], - "bias2" : [3, 18, 1], - "bias3" : [2, 4, 1], - "half_pload1": [3, 10, 1], - "half_pload3": [4, 9, 1], - "mim_cap_rows" : [1, 4, 1], - } - paramss = [] params_id = list(params.keys()) @@ -118,7 +118,6 @@ def closest_index(range_params, number): inputparam[22] = paramsss[14] inputparam[25] = paramsss[15] - result = safe_single_build_and_simulation(inputparam, hardfail=True) print(result) From 64817632c66b73b5c454d3610e1cacf60b725c22 Mon Sep 17 00:00:00 2001 From: Chetanya Goyal <95761876+chetanyagoyal@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:15:14 +0530 Subject: [PATCH 4/4] Revert quickstart.bash --- openfasoc/MLoptimization/quickstart.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openfasoc/MLoptimization/quickstart.bash b/openfasoc/MLoptimization/quickstart.bash index ffd6bb647..0344250f7 100644 --- a/openfasoc/MLoptimization/quickstart.bash +++ b/openfasoc/MLoptimization/quickstart.bash @@ -22,7 +22,7 @@ else fi # ===================================================================== -# check that ngspice>40 is installed +# check that ngspice 40 is installed ngspice --version > test_ngspice_version.txt version_line=$(sed -n '2p' test_ngspice_version.txt) expected_version="ngspice-40"