diff --git a/.github/workflows/regress.yml b/.github/workflows/regress.yml index eb07071d..f9b46bb8 100644 --- a/.github/workflows/regress.yml +++ b/.github/workflows/regress.yml @@ -19,31 +19,30 @@ jobs: run: | rm -rf ~/.local/lib/python3.8/site-packages/openram* make library - - name: Build conda - run: | - ./install_conda.sh - name: PDK Install run: | - export OPENRAM_HOME="${{ github.workspace }}/compiler" - export OPENRAM_TECH="${{ github.workspace }}/technology" - export PDK_ROOT="${{ github.workspace }}/pdk" + nix --extra-experimental-features 'nix-command flakes' develop --command bash -lc ' + export OPENRAM_HOME="${{ github.workspace }}/compiler"; + export OPENRAM_TECH="${{ github.workspace }}/technology"; + export PDK_ROOT="${{ github.workspace }}/pdk"; # Add make targets to install PDKs of all technologies that need it - make sky130-pdk + make sky130-pdk; make sky130-install - name: Regress run: | - export OPENRAM_HOME="${{ github.workspace }}/compiler" - export OPENRAM_TECH="${{ github.workspace }}/technology" - export PDK_ROOT="${{ github.workspace }}/pdk" - export FREEPDK45="~/FreePDK45" + nix --extra-experimental-features 'nix-command flakes' develop --command bash -lc ' + export OPENRAM_HOME="${{ github.workspace }}/compiler"; + export OPENRAM_TECH="${{ github.workspace }}/technology"; + export PDK_ROOT="${{ github.workspace }}/pdk"; + export FREEPDK45="~/FreePDK45"; # KLAYOUT_PATH breaks klayout installation. Unset it for now... - unset KLAYOUT_PATH + unset KLAYOUT_PATH; #cd $OPENRAM_HOME/.. && make pdk && make install #export OPENRAM_TMP="${{ github.workspace }}/scn4me_subm_temp" #python3-coverage run -p $OPENRAM_HOME/tests/regress.py -j 12 -t scn4m_subm #$OPENRAM_HOME/tests/regress.py -j 24 -t scn4m_subm - cd $OPENRAM_HOME/tests - make clean + cd $OPENRAM_HOME/tests; + make clean; make -k -j 48 - name: Archive if: ${{ failure() }} diff --git a/.gitignore b/.gitignore index a46b5394..7b0c7889 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,6 @@ compiler/tests/results/ open_pdks/ dist/ openram.egg-info/ -miniconda/ sky130A sky130B gf180mcuA diff --git a/MANIFEST.in b/MANIFEST.in index cb0967e3..790cc3e3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,7 +2,6 @@ include Makefile include openram.mk include setpaths.sh include requirements.txt -include install_conda.sh include docker/* recursive-include compiler * recursive-include technology * diff --git a/Makefile b/Makefile index d64c41b6..f2c65a15 100644 --- a/Makefile +++ b/Makefile @@ -57,8 +57,8 @@ INSTALL_BASE_DIRS := gds_lib mag_lib sp_lib lvs_lib calibre_lvs_lib klayout_lvs_ INSTALL_BASE := $(OPENRAM_HOME)/../technology/sky130 INSTALL_DIRS := $(addprefix $(INSTALL_BASE)/,$(INSTALL_BASE_DIRS)) -# If conda is installed, we will use ciel from there -CONDA_DIR := $(wildcard $(TOP_DIR)/miniconda) +# If nix is available, run ciel via nix develop +NIX_BIN := $(shell command -v nix 2>/dev/null) check-pdk-root: ifndef PDK_ROOT @@ -102,23 +102,21 @@ sky130-install: $(SRAM_LIB_DIR) sky130-pdk: $(SKY130_PDKS_DIR) @echo "Installing SKY130 via ciel..." -ifeq ($(CONDA_DIR),) +ifeq ($(NIX_BIN),) ciel enable --pdk sky130 $(SKY130_CIEL) else - source $(TOP_DIR)/miniconda/bin/activate && \ - ciel enable --pdk sky130 $(SKY130_CIEL) && \ - conda deactivate + nix --extra-experimental-features 'nix-command flakes' develop --command \ + ciel enable --pdk sky130 $(SKY130_CIEL) endif .PHONY: sky130-pdk gf180mcu-pdk: @echo "Installing GF180 via ciel..." -ifeq ($(CONDA_DIR),) +ifeq ($(NIX_BIN),) ciel enable --pdk gf180mcu $(GF180_CIEL) else - source $(TOP_DIR)/miniconda/bin/activate && \ - ciel enable --pdk gf180mcu $(GF180_CIEL) && \ - conda deactivate + nix --extra-experimental-features 'nix-command flakes' develop --command \ + ciel enable --pdk gf180mcu $(GF180_CIEL) endif .PHONY: gf180mcu-pdk diff --git a/__init__.py b/__init__.py index 758f50c4..48558a8a 100644 --- a/__init__.py +++ b/__init__.py @@ -23,22 +23,11 @@ if "OPENRAM_HOME" not in os.environ.keys(): __path__.insert(0, OPENRAM_HOME) -# Find the conda installer script -if os.path.exists(OPENRAM_HOME + "/install_conda.sh"): - CONDA_INSTALLER = OPENRAM_HOME + "/install_conda.sh" - CONDA_HOME = OPENRAM_HOME + "/miniconda" -elif os.path.exists(OPENRAM_HOME + "/../install_conda.sh"): - CONDA_INSTALLER = OPENRAM_HOME + "/../install_conda.sh" - CONDA_HOME = os.path.abspath(OPENRAM_HOME + "/../miniconda") -# Override CONDA_HOME if it's set as an environment variable -if "CONDA_HOME" in os.environ.keys(): - CONDA_HOME = os.environ["CONDA_HOME"] -# Add CONDA_HOME to environment variables just in case -try: - os.environ["CONDA_HOME"] = CONDA_HOME -except: - from openram import debug - debug.warning("Couldn't find conda setup directory.") +# Nix toolchain root (flake location) +NIX_HOME = os.path.abspath(OPENRAM_HOME + "/..") +if "NIX_HOME" in os.environ.keys(): + NIX_HOME = os.environ["NIX_HOME"] +os.environ["NIX_HOME"] = NIX_HOME # Import everything in globals.py diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index e5f44a2c..c184ed66 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -12,6 +12,7 @@ simulations as well. """ import os +import shlex import subprocess import numpy as np from openram import debug @@ -406,11 +407,15 @@ class stimuli(): spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w') spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w') - # Wrap the command with conda activate & conda deactivate + # Run spice in the Nix devShell when Nix-managed tools are enabled. # FIXME: Should use verify/run_script.py here but run_script doesn't return # the return code of the subprocess. File names might also mismatch. - from openram import CONDA_HOME - cmd = "/bin/bash -c 'source {0}/bin/activate && {1} && conda deactivate'".format(CONDA_HOME, cmd) + if OPTS.use_nix: + cmd = ( + "nix --extra-experimental-features 'nix-command flakes' " + "develop --command /bin/bash -lc {0}" + .format(shlex.quote(cmd)) + ) debug.info(2, cmd) proc = subprocess.run(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True) diff --git a/compiler/globals.py b/compiler/globals.py index 1c7d86f7..ea017aa6 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -188,7 +188,7 @@ def init_openram(config_file, is_unit_test=False): read_config(config_file, is_unit_test) - install_conda() + install_nix() import_tech() @@ -209,17 +209,40 @@ def init_openram(config_file, is_unit_test=False): from openram import verify -def install_conda(): - """ Setup conda for default tools. """ +def install_nix(): + """Initialize Nix-based toolchain dependencies.""" - # Don't setup conda if not used - if not OPTS.use_conda or OPTS.is_unit_test: + # Don't setup tools during unit tests. + if OPTS.is_unit_test: return - debug.info(1, "Creating conda setup..."); + if not OPTS.use_nix or OPTS.is_unit_test: + return - from openram import CONDA_INSTALLER - subprocess.call(CONDA_INSTALLER) + debug.info(1, "Bootstrapping toolchain with Nix...") + + nix_exe = shutil.which("nix") + if nix_exe is None: + debug.error("Nix is required for automatic tool setup, but 'nix' was not found in PATH.", -1) + + repo_root = os.path.abspath(os.path.join(OPENRAM_HOME, "..")) + flake_file = os.path.join(repo_root, "flake.nix") + if not os.path.exists(flake_file): + debug.error("Expected Nix flake at {} for tool setup.".format(flake_file), -1) + + # Trigger materialization/build of the devShell dependencies once. + # Environment activation still happens outside OpenRAM via `nix develop`. + cmd = [ + nix_exe, + "--extra-experimental-features", "nix-command flakes", + "develop", + "--command", "true", + ] + result = subprocess.call(cmd, cwd=repo_root) + if result != 0: + debug.error("Failed to initialize Nix toolchain (nix develop returned {}).".format(result), -1) + + return def setup_bitcell(): @@ -446,14 +469,7 @@ def find_exe(check_exe): Check if the binary exists in any path dir and return the full path. """ - # Search for conda setup if used - if OPTS.use_conda: - from openram import CONDA_HOME - search_path = "{0}/bin{1}{2}".format(CONDA_HOME, - os.pathsep, - os.environ["PATH"]) - else: - search_path = os.environ["PATH"] + search_path = os.environ["PATH"] # Check if the preferred spice option exists in the path for path in search_path.split(os.pathsep): diff --git a/compiler/options.py b/compiler/options.py index d97ee70e..229f7c3a 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -151,9 +151,9 @@ class options(optparse.Values): ################### # Top process that was ran (openram, memchar, memfunc) top_process = None - # Use conda to install the default tools - # (existing tools will be used if disabled) - use_conda = True + # Use Nix to initialize the default open-source toolchain. + # If disabled, OpenRAM uses whatever tools are already in PATH. + use_nix = True # Variable to select the variant of spice spice_name = None # The spice executable being used which is derived from the user PATH. diff --git a/compiler/verify/run_script.py b/compiler/verify/run_script.py index 10c12ad9..9dfef271 100644 --- a/compiler/verify/run_script.py +++ b/compiler/verify/run_script.py @@ -29,26 +29,21 @@ def run_script(cell_name, script="lvs"): scriptpath = '{0}run_{1}.sh'.format(OPTS.openram_temp, script) - # Wrap with conda activate & conda deactivate - if OPTS.use_conda: - from openram import CONDA_HOME - with open(scriptpath, "r") as f: - script_content = f.readlines() - with open(scriptpath, "w") as f: - # First line is shebang - f.write(script_content[0]) - # Activate conda using the activate script - f.write("source {}/bin/activate\n".format(CONDA_HOME)) - for line in script_content[1:]: - f.write(line) - # Deactivate conda at the end - f.write("conda deactivate\n") - debug.info(2, "Starting {}".format(scriptpath)) start = time.time() with open(outfile, 'wb') as fo, open(errfile, 'wb') as fe: + if OPTS.use_nix: + p_cmd = [ + "nix", + "--extra-experimental-features", "nix-command flakes", + "develop", + "--command", + scriptpath, + ] + else: + p_cmd = [scriptpath] p = subprocess.Popen( - [scriptpath], stdout=fo, stderr=fe, cwd=OPTS.openram_temp) + p_cmd, stdout=fo, stderr=fe, cwd=OPTS.openram_temp) if echo_cmd_output: tailo = subprocess.Popen([ diff --git a/docs/source/basic_setup.md b/docs/source/basic_setup.md index 2fc22ae0..daac3dc7 100644 --- a/docs/source/basic_setup.md +++ b/docs/source/basic_setup.md @@ -7,8 +7,8 @@ This page shows the basic setup for using OpenRAM to generate an SRAM. ## Table of Contents 1. [Dependencies](#dependencies) -1. [Anaconda](#anaconda) -1. [Docker](#docker-deprecated-use-anaconda-instead) +1. [Nix](#nix) +1. [Docker](#docker-deprecated-use-nix-instead) 1. [Environment](#environment) 1. [Sky130 Setup](#sky130-setup) @@ -20,51 +20,25 @@ In general, the OpenRAM compiler has very few dependencies: + Make + Python 3.5 or higher + Various Python packages (pip install -r requirements.txt) -+ Anaconda ++ Nix -## Anaconda -We use Anaconda package manager to install the tools used by OpenRAM. This way, -you don't have to worry about updating/installing these tools. OpenRAM installs -Anaconda silently in the background (without affecting any existing Anaconda -setup you have). +## Nix +OpenRAM uses Nix to provide the external toolchain (layout tools, simulators, +etc.) needed for SRAM generation. -You don't have to manually activate/deactivate the Anaconda environment. OpenRAM -automatically manages this before and after running the tools. - -OpenRAM uses Anaconda by default, but you can turn this feature off by setting -`use_conda = False` in your config file. Then, OpenRAM will use the tools you -have installed on your system. - -You can also tell OpenRAM where Anaconda should be installed or which Anaconda -setup it should use. You can set the `$CONDA_HOME` variable like this: +Enter the Nix development environment with: ``` -export CONDA_HOME="/path/to/conda/setup" +nix develop ``` -> **Note**: If you want to install Anaconda without running OpenRAM (for example -> to run unit tests, which do not install Anaconda), you can run: -> ``` -> ./install_conda.sh -> ``` +Within the devShell, required executables are available on `PATH` -> **Note**: You can uninstall OpenRAM's Anaconda installation by simply deleting -> the folder Anaconda is installed to. You can run: -> ``` -> rm -rf miniconda -> ``` +OpenRAM uses the `use_nix` option (enabled by default) to initialize Nix-based +tool dependencies via `nix develop`. -> **Note**: You can change a tool's version with the following commands: -> ``` -> source ./miniconda/bin/activate -> conda uninstall -> conda install -y -c vlsida-eda = -> ``` - - - -## Docker (deprecated, use Anaconda instead) +## Docker (deprecated, use Nix instead) We have a [docker setup](../../docker) to run OpenRAM. To use this, you should run: ``` @@ -124,12 +98,11 @@ make sky130-pdk This will use ciel to get the PDK. -> **Note**: If you don't have Magic installed, you need to install and activate -> the conda environment before running this command. You can run: +> **Note**: If you don't have Magic installed, enter the OpenRAM Nix devShell +> first (it provides Magic and other tools via `PATH`): > > ``` -> ./install_conda.sh -> source miniconda/bin/activate +> nix develop > ``` Then you must also install the [Sky130] SRAM build space with the appropriate diff --git a/docs/source/debug.md b/docs/source/debug.md index 2b6dde0d..e4326066 100644 --- a/docs/source/debug.md +++ b/docs/source/debug.md @@ -61,10 +61,9 @@ make -j 3 ``` The `-j` can run with 3 threads. By default, this will run in all technologies. -> **Note**: If you have not run openram before running unit tests, the conda -> environment will not be installed. You can install it by running -> `OpenRAM/install_conda.sh` (see [Basic Setup](basic_setup.md#anaconda) for -> more details). +> **Note**: The external EDA toolchain is provided by the Nix devShell. +> If you run unit tests without being in a Nix environment, enter it first: +> `nix develop` (see [Basic Setup](basic_setup.md#nix) for more details). To run a specific test in all technologies: ``` diff --git a/docs/source/index.md b/docs/source/index.md index bc7c5f50..4dc0d299 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -40,7 +40,7 @@ In general, the OpenRAM compiler has very few dependencies: + Make + Python 3.5 or higher + Various Python packages (pip install -r requirements.txt) -+ Anaconda ++ Nix Commercial tools (optional): * Spice Simulator diff --git a/docs/source/openram_dev_notes.md b/docs/source/openram_dev_notes.md index f2550cb0..99da300c 100644 --- a/docs/source/openram_dev_notes.md +++ b/docs/source/openram_dev_notes.md @@ -11,7 +11,7 @@ In this section, the detailed usage of using OpenRAM framework will be demonstra > > Before you go through, make sure that environment of Sky130 has been already set up. -1. Activate miniconda +1. Enter the Nix devShell 2. Edit the sram configuration file @@ -19,11 +19,11 @@ In this section, the detailed usage of using OpenRAM framework will be demonstra 4. Check the results -#### Activate miniconda +#### Enter Nix devShell ```bash cd OpenRAM/ -source ./miniconda/bin/activate +nix develop ``` #### Modified the sram configuration diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..e66c0499 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1777270315, + "narHash": "sha256-yKB4G6cKsQsWN7M6rZGk6gkJPDNPIzT05y4qzRyCDlI=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6368eda62c9775c38ef7f714b2555a741c20c72d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..1c0f0038 --- /dev/null +++ b/flake.nix @@ -0,0 +1,48 @@ +{ + description = "OpenRAM development environment (Nix)"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + }; + + outputs = { self, nixpkgs }: + let + systems = [ "x86_64-linux" ]; + forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system); + in + { + devShells = forAllSystems (system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + default = pkgs.mkShell { + packages = [ + # EDA / verification tools + pkgs.klayout + pkgs.magic-vlsi + # Use the LVS-focused netgen package; the generic netgen package + # may require a local build that can fail on some hosts. + pkgs.netgen-vlsi + pkgs.ngspice + pkgs.iverilog + pkgs.xyce + pkgs.xyce-parallel + pkgs.trilinos + pkgs.trilinos-mpi + + # Dev conveniences + pkgs.git + pkgs.gnumake + pkgs.curl + ]; + + shellHook = '' + export OPENRAM_USE_CONDA=0 + echo "OpenRAM: using tools from Nix devShell" + ''; + }; + }); + }; +} + diff --git a/install_conda.sh b/install_conda.sh deleted file mode 100755 index 63c5fd75..00000000 --- a/install_conda.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -#CONDA_INSTALLER_URL="https://repo.anaconda.com/miniconda/Miniconda3-py313_25.11.1-1-Linux-x86_64.sh" -#CONDA_INSTALLER_FILE="miniconda_installer_py313.sh" - -CONDA_INSTALLER_URL="https://repo.anaconda.com/miniconda/Miniconda3-py38_23.11.0-2-Linux-x86_64.sh" -CONDA_INSTALLER_FILE="miniconda_installer_py38.sh" -CONDA_HOME="${CONDA_HOME:-miniconda}" - -# The tool name format is "=". -# If you want to use the latest version, just use "". -TOOLS="" -TOOLS+="klayout=0.28.3 " -TOOLS+="magic=8.3.587 " -TOOLS+="netgen=1.5.286 " -TOOLS+="ngspice=26 " -TOOLS+="trilinos=12.12.1=1 " -TOOLS+="xyce=7.4" - -# Install miniconda if not already installed -if [[ ! -d "${CONDA_HOME}/bin" ]] -then - curl -s -o ${CONDA_INSTALLER_FILE} ${CONDA_INSTALLER_URL} - /bin/bash ${CONDA_INSTALLER_FILE} -b -p ${CONDA_HOME} - rm ${CONDA_INSTALLER_FILE} - source ${CONDA_HOME}/bin/activate - - # Prioritize channels to prevent version conflicts - conda config --add channels conda-forge - conda config --add channels vlsida-eda - - #conda install -q -y -c conda-forge trilinos - # Install rest of the tools from vlsida-eda - for tool in ${TOOLS} - do - conda install -q -y -c vlsida-eda ${tool} - done - - # Install iverilog from conda-eda - conda install -q -y -c litex-hub iverilog - # Install required Python packages - # (This step isn't required but used to prevent possible issues) - python3 -m pip install -r requirements.txt --ignore-installed - - conda deactivate -fi - diff --git a/setup.py b/setup.py index 34526279..1cae9b3c 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from setuptools import setup, find_namespace_packages # Include these folder from the root of repo as submodules include = ["compiler", "docker", "technology", "macros"] # Exclude files/folders with these words -exclude = ["docs", "images", "miniconda"] +exclude = ["docs", "images"] # Find all modules inside the 'compiler' folder