diff --git a/.github/workflows/conda_test.yaml b/.github/workflows/conda_test.yaml new file mode 100644 index 0000000..5dfb7ff --- /dev/null +++ b/.github/workflows/conda_test.yaml @@ -0,0 +1,97 @@ +# Testing if we can install Conda successfully. Sanity checking the CI flow. +name: conda-test + +on: [push] + +env: + CONDA_ENV_NAME: conda_test + +jobs: + conda-cache: + runs-on: ubuntu-20.04 + defaults: + run: + shell: bash -el {0} + continue-on-error: true + outputs: + pytestOutput: ${{ steps.unit-tests.outputs.test }} + strategy: + matrix: + python-version: ["3.7"] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + - name: Setup conda + uses: conda-incubator/setup-miniconda@v3.0.4 + with: + activate-environment: ${{ env.CONDA_ENV_NAME }} + python-version: ${{ matrix.python-version }} + auto-activate-base: false + - name: Save Conda Env + # Workaround, this isn't working otherwise. + run: | + echo "CONDA=$CONDA" + echo "CONDA=$CONDA" >> "$GITHUB_ENV" + - name: Cache Conda env + uses: actions/cache@v3 + with: + path: ${{ env.CONDA }}/envs/${{ env.CONDA_ENV_NAME }} + key: + conda-test-conda-${{ runner.os }}--${{ runner.arch }}--${{ + steps.get-date.outputs.today }}-${{ + hashFiles('conda_test.yml') }}-${{ + env.CACHE_NUMBER }} + env: + CACHE_NUMBER: 0 + id: cache + - name: Update environment + run: | + echo "CONDA=$CONDA" + conda env update -n ${{ env.CONDA_ENV_NAME }} -f conda_test.yml + ls $CONDA/envs/ + if: steps.cache.outputs.cache-hit != 'true' + conda-cache-load: + needs: conda-cache + runs-on: ubuntu-20.04 + defaults: + run: + shell: bash -el {0} + continue-on-error: true + outputs: + pytestOutput: ${{ steps.unit-tests.outputs.test }} + strategy: + matrix: + python-version: ["3.7"] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + - name: Setup conda + uses: conda-incubator/setup-miniconda@v3.0.4 + with: + activate-environment: ${{ env.CONDA_ENV_NAME }} + python-version: ${{ matrix.python-version }} + auto-activate-base: false + - name: Save Conda Env + # Workaround, this isn't working otherwise. + run: | + echo "CONDA=$CONDA" + echo "CONDA=$CONDA" >> "$GITHUB_ENV" + - name: Cache Conda env + uses: actions/cache@v3 + with: + path: ${{ env.CONDA }}/envs/${{ env.CONDA_ENV_NAME }} + key: + conda-test-conda-${{ runner.os }}--${{ runner.arch }}--${{ + steps.get-date.outputs.today }}-${{ + hashFiles('conda_test.yml') }}-${{ + env.CACHE_NUMBER }} + env: + CACHE_NUMBER: 0 + id: cache + - name: Fail + run: exit 1 + if: steps.cache.outputs.cache-hit != 'true' diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml new file mode 100644 index 0000000..f8f8581 --- /dev/null +++ b/.github/workflows/pr.yaml @@ -0,0 +1,157 @@ +# CI flow +name: pybag-pr-ci + +on: + push: + schedule: + - cron: '0 9 * * *' # 9AM UTC = 2AM PST + +env: + CONDA_ENV_NAME: bag_py3d7_c + +jobs: + build: + runs-on: ubuntu-20.04 + defaults: + run: + shell: bash -el {0} + continue-on-error: true + outputs: + pytestOutput: ${{ steps.unit-tests.outputs.test }} + strategy: + matrix: + python-version: ["3.7"] + steps: + - name: Install packages + run: | + sudo apt update + sudo apt -y install \ + autoconf \ + curl \ + gcc-8 \ + g++-8 \ + git \ + libtool \ + libltdl-dev \ + pkg-config \ + make \ + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Setup conda + uses: conda-incubator/setup-miniconda@v3.0.4 + with: + activate-environment: ${{ env.CONDA_ENV_NAME }} + python-version: ${{ matrix.python-version }} + auto-activate-base: false + - name: Save Conda Env + # Workaround, this isn't working otherwise. + run: | + echo "CONDA=$CONDA" + echo "CONDA=$CONDA" >> "$GITHUB_ENV" + - name: Cache Conda env + uses: actions/cache@v3 + with: + path: ${{ env.CONDA }}/envs/${{ env.CONDA_ENV_NAME }} + key: + conda-${{ runner.os }}--${{ runner.arch }}--${{ + steps.get-date.outputs.today }}-${{ + hashFiles('environment.yml') }}-${{ + env.CACHE_NUMBER }} + env: + # Increase this value to reset cache if etc/example-environment.yml has not changed + CACHE_NUMBER: 0 + id: cache-conda + - name: Update conda environment + run: | + echo "CONDA=$CONDA" + conda env update -n ${{ env.CONDA_ENV_NAME }} -f environment.yml + if: steps.cache-conda.outputs.cache-hit != 'true' + - name: Install additional dependencies + if: steps.cache-conda.outputs.cache-hit != 'true' + run: | + cd setup + mkdir install + cp setup_script.sh install/ + cp render_template.py install/ + cp project-config.template install/ + cd install + ./setup_script.sh + cd ../../ + - name: Build cbag and pybag + id: cpp-build + run: | + export PYBAG_PYTHON=python + echo "PYBAG_PYTHON=$PYBAG_PYTHON" + ./run_test.sh + - name: Cache pybag build + uses: actions/cache@v3 + with: + path: _build + key: + build-${{ runner.os }}--${{ runner.arch }}--${{ + steps.get-date.outputs.today }}-${{ + env.CACHE_NUMBER }} + env: + # Increase this value to reset cache if etc/example-environment.yml has not changed + CACHE_NUMBER: 0 + id: cache-pybag + test: + needs: build + runs-on: ubuntu-20.04 + defaults: + run: + shell: bash -el {0} + continue-on-error: true + outputs: + pytestOutput: ${{ steps.unit-tests.outputs.test }} + strategy: + matrix: + python-version: ["3.7"] + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Setup conda + uses: conda-incubator/setup-miniconda@v3.0.4 + with: + activate-environment: bag_py3d7_c + python-version: ${{ matrix.python-version }} + auto-activate-base: false + - name: Save Conda Env + # Workaround, this isn't working otherwise. + run: | + echo "CONDA=$CONDA" + echo "CONDA=$CONDA" >> "$GITHUB_ENV" + - name: Cache Conda env + uses: actions/cache@v3 + with: + path: ${{ env.CONDA }}/envs/${{ env.CONDA_ENV_NAME }} + key: + conda-${{ runner.os }}--${{ runner.arch }}--${{ + steps.get-date.outputs.today }}-${{ + hashFiles('environment.yml') }}-${{ + env.CACHE_NUMBER }} + env: + # Increase this value to reset cache if etc/example-environment.yml has not changed + CACHE_NUMBER: 0 + id: cache-conda + - name: Cache pybag build + uses: actions/cache@v3 + with: + path: _build + key: + build-${{ runner.os }}--${{ runner.arch }}--${{ + steps.get-date.outputs.today }}-${{ + env.CACHE_NUMBER }} + env: + # Increase this value to reset cache if etc/example-environment.yml has not changed + CACHE_NUMBER: 0 + id: cache-pybag + - name: Run test + id: run-pytest + run: | + which pytest + pytest tests -v diff --git a/.gitignore b/.gitignore index d2020f0..58f0cb7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ __pycache__ # build related files _build .pytest_cache +src/pybag/core.pyi diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e0e936..dd1ba31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,7 +91,12 @@ add_subdirectory(pybind11_generics) find_package(yaml-cpp REQUIRED CONFIG) # add python bindings for cbag -pybind11_add_module(core +if(DEFINED ENV{OA_LINK_DIR}) + # Optionally support OA libraries + message("INFO: Building pybag with OpenAccess support.") + message("OA include directory: " $ENV{OA_INCLUDE_DIR}) + message("OA link directory: " $ENV{OA_LINK_DIR}) + pybind11_add_module(core ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/bbox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/bbox_array.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/bbox_collection.cpp @@ -111,6 +116,29 @@ pybind11_add_module(core ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/tech.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/transform.cpp ) + target_compile_definitions(core PUBLIC OA_LINK_DIR=$ENV{OA_LINK_DIR}) +else() + message("INFO: Building pybag without OpenAccess support.") + pybind11_add_module(core + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/bbox.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/bbox_array.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/bbox_collection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/core.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/enum_conv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/gds.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/geometry.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/grid.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/interval.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/lay_objects.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/layout.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/logging.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/name.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/rtree.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/schematic.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/tech.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/pybag/transform.cpp + ) +endif() target_include_directories(core PUBLIC diff --git a/README.md b/README.md index 1bdcb80..1166093 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,31 @@ # PYBAG -Python wrapper for [CBAG](https://github.com/bluecheetah/cbag) library. +Python wrapper for [cbag](https://github.com/ucb-art/cbag) library for BAG3++. -This is a fork of [PYBAG] (https://github.com/ucb-art/pybag), which as of May 13th, 2019, -has been taken offline temporarily. +Pybag provides `enum.py` and `core.pyi` as an interface between `BAG` and `cbag`. +`core.pyi` is created during `cbag` compilation and is not tracked by Git. + +## Setup + +This library should be used with [BAG3++](https://github.com/ucb-art/bag). To setup BAG, follow the instructions outlined on the [RTD page](https://bag3-readthedocs.readthedocs.io/en/latest/dependencies/). + +Alternative, if you are using Ubuntu, you can use the `setup_script.sh` under the `setup` directory. This runs the steps described in the RTD page above. See the `setup/README.md` for discussion. + +To build the `pybag` library, run `./run_test.sh`. This will compile `cbag` as well as create the `pybag` Python wrappers for `BAG`. + +Running `./run_test.sh`requires the `PYBAG_PYTHON` environment variable be set to the Python from your Miniconda install from the setup process. + +### Building with OpenAccess Libraries + +Pybag (and the underlying cbag) can be compiled with the OpenAccess (OA) C++ libraries from Si2 to provide minor acceleration in creating OA views for tools such as Cadence Virtuoso. These libraries are **NOT required** for using BAG in general. If the OA libraries are not included, BAG can still create OA views using SKILL commands. + +To compile with these libraries, the following environment variables must be set: +- `OA_INCLUDE_DIR`: Include directory. +- `OA_LINK_DIR`: Link directory. + +Compiling with the OA libraries will add the `PyOADatabase` class to `core.pyi` after compile time. If `PyOADatabase` does not exist, then you did not compile with the OA libraries. + +Pybag has been tested with the OpenAccess 2.2 API. These features are included for legacy compatibility and are not actively maintained. ## Licensing diff --git a/build.sh b/build.sh index b16e01f..db892ec 100755 --- a/build.sh +++ b/build.sh @@ -46,8 +46,9 @@ if [ -z ${OA_LINK_DIR+x} ] then - echo "OA_LINK_DIR is unset" - exit 1 + echo "OA_LINK_DIR is unset. Building pybag without OpenAccess support." +else + echo "OA_LINK_DIR is set. Building pybag with OpenAccess support." fi if [ -z ${PYBAG_PYTHON+x} ] diff --git a/build_util.sh b/build_util.sh index af89af9..1b2cb2b 100755 --- a/build_util.sh +++ b/build_util.sh @@ -46,8 +46,9 @@ if [ -z ${OA_LINK_DIR+x} ] then - echo "OA_LINK_DIR is unset" - exit 1 + echo "OA_LINK_DIR is unset. Building pybag without OpenAccess support." +else + echo "OA_LINK_DIR is set. Building pybag with OpenAccess support." fi export NUM_CORES=$(nproc --all) diff --git a/cbag b/cbag index 4e05a5d..84d99c9 160000 --- a/cbag +++ b/cbag @@ -1 +1 @@ -Subproject commit 4e05a5d6fba26a30c4fb7437c6e4e13314c15f3b +Subproject commit 84d99c9fe0e8ac9fb47677e925df265d8c504357 diff --git a/conda_test.yml b/conda_test.yml new file mode 100644 index 0000000..f9570d7 --- /dev/null +++ b/conda_test.yml @@ -0,0 +1,8 @@ +name: conda_test +channels: + - anaconda + - conda-forge + - defaults +dependencies: + - numpy-base>=1.21.5 + \ No newline at end of file diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..ba53e68 --- /dev/null +++ b/environment.yml @@ -0,0 +1,137 @@ +name: bag_py3d7_c +channels: + - anaconda + - conda-forge + - defaults +dependencies: + - _libgcc_mutex=0.1=main + - _openmp_mutex=4.5=1_gnu + - astroid=2.11.7=py37h06a4308_0 + - blas=1.0=mkl + - brotli=1.0.9=he6710b0_2 + - ca-certificates=2022.12.7=ha878542_0 + - certifi=2022.12.7=pyhd8ed1ab_0 + - dbus=1.13.18=hb2f20db_0 + - dill=0.3.6=py37h06a4308_0 + - expat=2.4.4=h295c915_0 + - flit-core=3.6.0=pyhd3eb1b0_0 + - fmt=8.0.1=h4bd325d_0 + - fontconfig=2.13.1=h6c09931_0 + - fonttools=4.25.0=pyhd3eb1b0_0 + - freetype=2.11.0=h70c0345_0 + - giflib=5.2.1=h7b6447c_0 + - glib=2.56.2=hd408876_0 + - gst-plugins-base=1.14.0=hbbd80ab_1 + - gstreamer=1.14.0=hb453b48_1 + - icu=58.2=he6710b0_3 + - intel-openmp=2021.4.0=h06a4308_3561 + - isort=5.9.3=pyhd3eb1b0_0 + - jpeg=9e=h7f8727e_0 + - lazy-object-proxy=1.6.0=py37h27cfd23_0 + - lcms2=2.12=h3be6417_0 + - ld_impl_linux-64=2.35.1=h7274673_9 + - libffi=3.3=he6710b0_2 + - libgcc-ng=9.3.0=h5101ec6_17 + - libgomp=9.3.0=h5101ec6_17 + - libpng=1.6.37=hbc83047_0 + - libstdcxx-ng=9.3.0=hd4cf53a_17 + - libtiff=4.2.0=h85742a9_0 + - libuuid=1.0.3=h7f8727e_2 + - libwebp=1.2.2=h55f646e_0 + - libwebp-base=1.2.2=h7f8727e_0 + - libxcb=1.15=h7f8727e_0 + - libxml2=2.9.12=h03d6c58_0 + - lz4-c=1.9.3=h295c915_1 + - matplotlib-base=3.5.1=py37ha18d171_1 + - mccabe=0.7.0=pyhd3eb1b0_0 + - mkl=2021.4.0=h06a4308_640 + - mkl-service=2.4.0=py37h7f8727e_0 + - mkl_fft=1.3.1=py37hd3c417c_0 + - mkl_random=1.2.2=py37h51133e4_0 + - munkres=1.1.4=py_0 + - ncurses=6.3=h7f8727e_2 + - numpy-base=1.21.5=py37hf524024_2 + - openssl=1.1.1k=h7f98852_0 + - pcre=8.45=h295c915_0 + - pillow=9.0.1=py37h22f2fdc_0 + - pip=21.2.2=py37h06a4308_0 + - platformdirs=2.5.2=py37h06a4308_0 + - pylint=2.14.5=py37h06a4308_0 + - pyqt=5.9.2=py37h05f1152_2 + - python=3.7.7=hcff3b4d_5 + - qt=5.9.7=h5867ecd_1 + - readline=8.1.2=h7f8727e_1 + - setuptools=58.0.4=py37h06a4308_0 + - spdlog=1.9.1=h4bd325d_0 + - sqlite=3.37.2=hc218d9a_0 + - tk=8.6.11=h1ccaba5_0 + - tomli=2.0.1=py37h06a4308_0 + - tomlkit=0.11.1=py37h06a4308_0 + - tornado=6.1=py37h27cfd23_0 + - typed-ast=1.4.3=py37h7f8727e_1 + - typing_extensions=4.4.0=py37h06a4308_0 + - wheel=0.37.1=pyhd3eb1b0_0 + - wrapt=1.13.3=py37h7f8727e_2 + - xz=5.2.5=h7b6447c_0 + - zlib=1.2.11=h7f8727e_4 + - zstd=1.4.9=haebb681_0 + - pip: + - apipkg==1.5 + - appdirs==1.4.3 + - argparse==1.4.0 + - arrow==1.2.2 + - attrs==19.3.0 + - backcall==0.1.0 + - cycler==0.10.0 + - decorator==4.4.2 + - distlib==0.3.0 + - docopt==0.6.2 + - execnet==1.7.1 + - filelock==3.0.12 + - h5py==2.10.0 + - importlib-metadata==1.6.0 + - inform==1.26.0 + - ipython==7.13.0 + - ipython-genutils==0.2.0 + - jedi==0.16.0 + - jinja2==2.11.1 + - kiwisolver==1.2.0 + - libpsf==0.1.3 + - markupsafe==1.1.1 + - matplotlib==3.2.1 + - more-itertools==8.2.0 + - networkx==2.4 + - numpy==1.18.2 + - packaging==20.3 + - pandocfilters==1.4.2 + - parso==0.6.2 + - pexpect==4.8.0 + - pickleshare==0.7.5 + - pluggy==0.13.1 + - ply==3.4 + - prompt-toolkit==3.0.5 + - psf-utils==1.5.0 + - ptyprocess==0.6.0 + - py==1.8.1 + - pygments==2.6.1 + - pyparsing==2.4.6 + - pyqt5==5.9 + - pyqt5-sip==12.7.1 + - pytest==5.4.1 + - pytest-forked==1.1.3 + - pytest-xdist==1.31.0 + - python-dateutil==2.8.1 + - pyzmq==19.0.0 + - quantiphy==2.17.0 + - ruamel-yaml==0.16.10 + - ruamel-yaml-clib==0.2.0 + - scipy==1.4.1 + - sip==4.19.8 + - six==1.14.0 + - sortedcontainers==2.1.0 + - traitlets==4.3.3 + - typing-extensions==4.1.1 + - virtualenv==20.0.15 + - wcwidth==0.1.9 + - zipp==3.1.0 + - zmq==0.0.0 \ No newline at end of file diff --git a/setup/.gitignore b/setup/.gitignore new file mode 100644 index 0000000..e9c0271 --- /dev/null +++ b/setup/.gitignore @@ -0,0 +1 @@ +install* \ No newline at end of file diff --git a/setup/README.md b/setup/README.md new file mode 100644 index 0000000..683ee14 --- /dev/null +++ b/setup/README.md @@ -0,0 +1,20 @@ +# Setup script for Pybag + +These scripts follow the instructions outlined on the [RTD page](https://bag3-readthedocs.readthedocs.io/en/latest/dependencies/). + +NOTE: this setup has only been tested on Ubuntu 20.04. Support for 22.04 has not been formally verified. + +## Files +- `README.md`: this file +- `setup_script.sh`: run script. Installs the dependencies as decribed in the RTD. +- `project-config.template`: a Jinja render template for creating the `project-config.jam` file for the Boost library. +- `render_template.py`: script to render `project-config.template` + +## Usage +1) Install the following dependencies using `apt`: +`autoconf curl gcc-8 g++-8 git libtool libltdl-dev pkg-config make` +2) Install [Miniconda](https://docs.anaconda.com/miniconda/miniconda-install/) and create the environment using the [environment.yml](https://github.com/ucb-art/pybag/environment.yml) file. +3) Make an `install` directory to install the dependencies and copy all the files from here to `install`. +4) Run `./setup_script.sh` + +Now you are ready to build `pybag`. diff --git a/setup/project-config.template b/setup/project-config.template new file mode 100644 index 0000000..83868df --- /dev/null +++ b/setup/project-config.template @@ -0,0 +1,38 @@ +# Boost.Build Configuration +# Based on autogenerated file from bootstrap.sh + +import option ; +import feature ; + +# Compiler configuration. This definition will be used unless +# you already have defined some toolsets in your user-config.jam +# file. +if ! gcc in [ feature.values ] +{ + using gcc : 8 : gcc-8 : ; +} + +project : default-build gcc ; + +# Python configuration +import python ; +if ! [ python.configured ] +{ + using python : 3.7 : {{ conda_env_path }} : {{ conda_env_path }}/include/python3.7m ; +} + +# List of --with- and --without- +# options. If left empty, all libraries will be built. +# Options specified on the command line completely +# override this variable. +libraries = ; + +# These settings are equivalent to corresponding command-line +# options. +option.set prefix : {{ conda_env_path }} ; +option.set exec-prefix : {{ conda_env_path }} ; +option.set libdir : {{ conda_env_path }}/lib ; +option.set includedir : {{ conda_env_path }}/include ; + +# Stop on first error +option.set keep-going : false ; \ No newline at end of file diff --git a/setup/render_template.py b/setup/render_template.py new file mode 100644 index 0000000..c74c03d --- /dev/null +++ b/setup/render_template.py @@ -0,0 +1,22 @@ +""" +Render a project-config.jam file with the correct Python (conda) pointers +""" +import os +from jinja2 import Template + +# Set by setup_script.sh +conda_env_path = os.environ['CONDA_ENV_PATH'] + +template_fname = "project-config.template" +output_fname = "project-config.jam" + +fin = open(template_fname, 'r') +raw_contents = fin.read() +fin.close() + +pop_contents = Template(raw_contents).render(conda_env_path=conda_env_path) + +fout = open(output_fname, 'w') +fout.write(pop_contents) +fout.write("\n") +fout.close() \ No newline at end of file diff --git a/setup/setup_script.sh b/setup/setup_script.sh new file mode 100755 index 0000000..ff0cbc7 --- /dev/null +++ b/setup/setup_script.sh @@ -0,0 +1,65 @@ +# Setup script + +# CHANGE THIS +export CONDA_ENV_PATH=${CONDA}/envs/bag_py3d7_c + +export CC=gcc-8 +export CXX=g++-8 + +# cmake +wget https://github.com/Kitware/CMake/releases/download/v3.17.0/cmake-3.17.0.tar.gz +tar -xvf cmake-3.17.0.tar.gz +cd cmake-3.17.0 +./bootstrap --prefix=$CONDA_ENV_PATH --parallel=4 +make -j4 +make install +cd ../ + +# magic enum +git clone https://github.com/Neargye/magic_enum.git +cd magic_enum +cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release -DMAGIC_ENUM_OPT_BUILD_EXAMPLES=FALSE -DMAGIC_ENUM_OPT_BUILD_TESTS=FALSE -DCMAKE_INSTALL_PREFIX=$CONDA_ENV_PATH +cmake --build build +cd build +make install +cd ../../ + +# yaml cpp +git clone https://github.com/jbeder/yaml-cpp.git +cd yaml-cpp +cmake -B_build -H. -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_PREFIX=$CONDA_ENV_PATH +cmake --build _build --target install -- -j 4 +cd ../ + +# libfyaml +git clone https://github.com/pantoniou/libfyaml.git +cd libfyaml +./bootstrap.sh +./configure --prefix=$CONDA_ENV_PATH +make -j12 +make install +cd ../ + +# HDF5 1.10 +wget https://support.hdfgroup.org/ftp/HDF5/releases/hdf5-1.10/hdf5-1.10.6/src/hdf5-1.10.6.tar.gz +tar -xvf hdf5-1.10.6.tar.gz +cd hdf5-1.10.6 +./configure --prefix=$CONDA_ENV_PATH +make -j24 +make install +cd ../ + +# Boost +wget https://boostorg.jfrog.io/artifactory/main/release/1.72.0/source/boost_1_72_0.tar.gz +tar -xvf boost_1_72_0.tar.gz +cd boost_1_72_0 +./bootstrap.sh --prefix=$CONDA_ENV_PATH --without-icu + +# edit project-config.jam +# Create by rendering python +cd ../ +python render_template.py + +# TODO: run ./b2 +cd boost_1_72_0 +./b2 --build-dir=_build cxxflags=-fPIC -j8 -target=shared,static --with-filesystem --with-serialization --with-program_options --project-config=../project-config.jam install | tee install.log \ No newline at end of file diff --git a/src/pybag/core.cpp b/src/pybag/core.cpp index 38a6097..e4c946f 100644 --- a/src/pybag/core.cpp +++ b/src/pybag/core.cpp @@ -52,7 +52,11 @@ limitations under the License. #include #include #include -#include +#ifdef OA_LINK_DIR + #include +#else + #include +#endif #include #include #include @@ -75,7 +79,11 @@ PYBIND11_MODULE(core, m) { // make sure tech/routing grid are defined before binding gds bind_gds(m); // make sure routing grid and tr_colors are defined before binding OA - bind_oa(m); + #ifdef OA_LINK_DIR + bind_oa(m); + #else + bind_schematic(m); + #endif bind_rtree(m); bind_logging(m); bind_name(m); diff --git a/src/pybag/core.pyi b/src/pybag/core.pyi deleted file mode 100644 index b281756..0000000 --- a/src/pybag/core.pyi +++ /dev/null @@ -1,586 +0,0 @@ -# -*- coding: utf-8 -*- -# Stubs for pybag.core (Python 3.7) -# -# NOTE: This dynamically typed stub was automatically generated by stubgen. - -from builtins import int -from builtins import str -from pybag.enum import LogLevel -from pybag.enum import Orient2D -from pybag.enum import SigType -from pybag.enum import Orientation -from typing import overload -from typing import Optional -from typing import Tuple -from typing import Iterator -from typing import Any -from typing import List -from typing import Iterable -from typing import Union - -COORD_MAX: int = ... -COORD_MIN: int = ... -SUPPLY_SUFFIX: str = ... - - -def convert_cdba_name_bit(name: str, design_output_code: int = 2) -> str: ... - - -def coord_to_custom_htr(coord: int, pitch: int, off: int, round_mode: int, even: bool) -> int: ... - - -def gds_equal(lhs_file: str, rhs_file: str) -> bool: ... - - -def get_bag_logger() -> FileLogger: ... - - -def get_cdba_name_bits(name: str, design_output_code: int = 2) -> List[str]: ... - - -def get_cv_header(cv: PySchCellView, cell_name: str, fmt_code: int) -> str: ... - - -def get_wire_iterator(grid: PyRoutingGrid, tr_colors: TrackColoring, tid: PyTrackID, lower: int, upper: int) -> Iterator[Tuple[str, str, BBox]]: ... - - -def implement_gds(fname: str, lib_name: str, layer_map: str, obj_map: str, cv_list: Iterable[Tuple[str, PyLayCellView]]) -> None: ... - - -def implement_netlist(fname: str, content_list: List[Tuple[str, Tuple[PySchCellView, str]]], top_list: List[str], fmt_code: int, flat: bool, shell: bool, top_subckt: bool, square_bracket: bool, rmin: int, precision: int, sup_code: int, prim_fname: str, cv_info_list: List[PySchCellViewInfo], cv_netlist_list: List[str], cv_info_out: Optional[List[PySchCellViewInfo]], va_cvinfo_list: List[PySchCellViewInfo]) -> None: ... - - -def implement_yaml(fname: str, content_list: Iterable[Tuple[str, Tuple[PySchCellView, str]]]) -> None: ... - - -def make_tr_colors(tech: PyTech) -> TrackColoring: ... - - -def read_gds(fname: str, layer_map: str, obj_map: str, grid: PyRoutingGrid, tr_colors: TrackColoring) -> List[PyLayCellView]: ... - - -class BBox: - @property - def h(self) -> int: ... - @property - def w(self) -> int: ... - @property - def xh(self) -> int: ... - @property - def xl(self) -> int: ... - @property - def xm(self) -> int: ... - @property - def yh(self) -> int: ... - @property - def yl(self) -> int: ... - @property - def ym(self) -> int: ... - @overload - def __init__(self, xl: int, yl: int, xh: int, yh: int) -> None: ... - @overload - def __init__(self, orient_code: int, tl: int, th: int, pl: int, ph: int) -> None: ... - def __eq__(self, other: BBox) -> bool: ... - def expand(self, dx: int = 0, dy: int = 0) -> BBox: ... - def extend(self, x: Optional[int] = None, y: Optional[int] = None) -> BBox: ... - def extend_orient(self, orient_code: int, ct: Optional[int] = None, cp: Optional[int] = None) -> BBox: ... - def flip_xy(self) -> BBox: ... - def get_center(self, orient_code: int) -> int: ... - def get_coord(self, orient_code: int, bnd_code: bool) -> int: ... - def get_dim(self, orient_code: int) -> int: ... - def get_expand(self, dx: int = 0, dy: int = 0) -> BBox: ... - def get_extend(self, x: Optional[int] = None, y: Optional[int] = None) -> BBox: ... - def get_extend_orient(self, orient_code: int, ct: Optional[int] = None, cp: Optional[int] = None) -> BBox: ... - def get_flip_xy(self) -> BBox: ... - def get_immutable_key(self) -> Tuple[int, int, int, int]: ... - def get_intersect(self, bbox: BBox) -> BBox: ... - def get_interval(self, orient_code: int) -> Tuple[int, int]: ... - @staticmethod - def get_invalid_bbox() -> BBox: ... - def get_merge(self, bbox: BBox) -> BBox: ... - def get_move_by(self, dx: int = 0, dy: int = 0) -> BBox: ... - def get_move_by_orient(self, orient_code: int, dt: int = 0, dp: int = 0) -> BBox: ... - def get_transform(self, xform: Transform) -> BBox: ... - def intersect(self, bbox: BBox) -> BBox: ... - def is_physical(self) -> bool: ... - def is_valid(self) -> bool: ... - def merge(self, bbox: BBox) -> BBox: ... - def move_by(self, dx: int = 0, dy: int = 0) -> BBox: ... - def move_by_orient(self, orient_code: int, dt: int = 0, dp: int = 0) -> BBox: ... - def overlaps(self, bbox: BBox) -> bool: ... - def set_interval(self, orient_code: int, lo: int, hi: int) -> BBox: ... - def transform(self, xform: Transform) -> BBox: ... - - -class BBoxArray: - @property - def base(self) -> BBox: ... - @property - def bound_box(self) -> BBox: ... - @property - def nx(self) -> int: ... - @property - def ny(self) -> int: ... - @property - def spx(self) -> int: ... - @property - def spy(self) -> int: ... - @property - def xh(self) -> int: ... - @property - def xl(self) -> int: ... - @property - def xm(self) -> int: ... - @property - def yh(self) -> int: ... - @property - def yl(self) -> int: ... - @property - def ym(self) -> int: ... - @overload - def __init__(self, base: BBox, nx: int = 1, ny: int = 1, spx: int = 0, spy: int = 0) -> None: ... - @overload - def __init__(self, base: BBox, orient_code: int, nt: int = 1, spt: int = 0, np: int = 1, spp: int = 0) -> None: ... - def __eq__(self, other: BBoxArray) -> bool: ... - def __iter__(self) -> Iterator[BBox]: ... - def extend_orient(self, orient_code: int, ct: Optional[int] = None, cp: Optional[int] = None) -> BBoxArray: ... - def get_array_info(self, orient_code: int) -> Tuple[int, int]: ... - def get_bbox(self, idx: int) -> BBox: ... - def get_coord(self, orient_code: int, bnd_code: bool) -> int: ... - def get_copy(self) -> BBoxArray: ... - def get_extend_orient(self, orient_code: int, ct: Optional[int] = None, cp: Optional[int] = None) -> BBoxArray: ... - def get_move_by(self, dx: int = 0, dy: int = 0) -> BBoxArray: ... - def get_num(self, orient_code: int) -> int: ... - def get_sp(self, orient_code: int) -> int: ... - def get_sub_array(self, orient_code: int, div_num: int, idx: int) -> BBoxArray: ... - def get_transform(self, xform: Transform) -> BBoxArray: ... - def move_by(self, dx: int = 0, dy: int = 0) -> BBoxArray: ... - def set_interval(self, orient_code: int, lo: int, hi: int) -> BBoxArray: ... - def transform(self, xform: Transform) -> BBoxArray: ... - - -class BBoxCollection: - def __init__(self) -> None: ... - def __iter__(self) -> Iterator[BBoxArray]: ... - def __len__(self) -> int: ... - def add_rect_arr(self, box: BBox, nx: int = 1, ny: int = 1, spx: int = 0, spy: int = 0) -> None: ... - - -class FileLogger: - @property - def flush_level(self) -> LogLevel: ... - @property - def level(self) -> LogLevel: ... - @property - def log_basename(self) -> str: ... - def __init__(self, name: str, log_fname: str, stdout_level: int) -> None: ... - def critical(self, msg: str) -> None: ... - def debug(self, msg: str) -> None: ... - def error(self, msg: str) -> None: ... - def flush(self) -> None: ... - def flush_on(self, level: int) -> None: ... - def info(self, msg: str) -> None: ... - def log(self, level: int, msg: str) -> None: ... - def set_level(self, level: int) -> None: ... - def trace(self, msg: str) -> None: ... - def warn(self, msg: str) -> None: ... - - -class PyBlockage: - def __init__(self) -> None: ... - def commit(self) -> None: ... - - -class PyBoundary: - def __init__(self) -> None: ... - def commit(self) -> None: ... - - -class PyDisjointIntervals: - @property - def start(self) -> int: ... - @property - def stop(self) -> int: ... - def __init__(self) -> None: ... - def __bool__(self) -> bool: ... - def __contains__(self, key: Tuple[int, int]) -> bool: ... - def __iter__(self) -> Iterator[Tuple[int, int]]: ... - def __len__(self) -> int: ... - def add(self, intv: Tuple[int, int], val: Any = None, merge: bool = False, abut: bool = False, check_only: bool = False) -> bool: ... - @overload - def covers(self, key: Tuple[int, int]) -> bool: ... - @overload - def covers(self, key: int) -> bool: ... - def get_complement(self, total_intv: Tuple[int, int]) -> PyDisjointIntervals: ... - def get_copy(self) -> PyDisjointIntervals: ... - def get_first_overlap_item(self, key: Tuple[int, int]) -> Optional[Tuple[Tuple[int, int], Any]]: ... - def get_intersection(self, other: PyDisjointIntervals) -> PyDisjointIntervals: ... - def get_next(self, val: int, even: bool = True) -> int: ... - def get_prev(self, val: int, even: bool = True) -> int: ... - def get_transform(self, scale: int = 1, shift: int = 0) -> PyDisjointIntervals: ... - def intervals(self) -> Iterator[Tuple[int, int]]: ... - def items(self) -> Iterator[Tuple[Tuple[int, int], Any]]: ... - def overlap_intervals(self, key: Tuple[int, int]) -> Iterator[Tuple[int, int]]: ... - def overlap_items(self, key: Tuple[int, int]) -> Iterator[Tuple[Tuple[int, int], Any]]: ... - def overlap_values(self, key: Tuple[int, int]) -> Iterator[Any]: ... - def overlaps(self, key: Tuple[int, int]) -> bool: ... - def remove(self, key: Tuple[int, int]) -> bool: ... - def remove_overlaps(self, key: Tuple[int, int]) -> bool: ... - def subtract(self, key: Tuple[int, int]) -> bool: ... - def values(self) -> Iterator[Any]: ... - - -class PyLayCellView: - @property - def cell_name(self) -> str: ... - @property - def is_empty(self) -> bool: ... - def __init__(self, grid: PyRoutingGrid, tr_colors: TrackColoring, cell_name: str) -> None: ... - def __eq__(self, other: PyLayCellView) -> bool: ... - def add_blockage(self, layer: str, blk_code: int, points: List[Tuple[int, int]], commit: bool) -> PyBlockage: ... - def add_boundary(self, bnd_code: int, points: List[Tuple[int, int]], commit: bool) -> PyBoundary: ... - def add_instance(self, cv: PyLayCellView, name: str, xform: Transform, nx: int, ny: int, spx: int, spy: int, commit: bool) -> PyLayInstRef: ... - def add_label(self, layer: str, purpose: str, xform: Transform, label: str, height: int) -> None: ... - def add_path(self, layer: str, purpose: str, points: List[Tuple[int, int]], half_width: int, style0: int, style1: int, stylem: int, commit: bool) -> PyPath: ... - def add_path45_bus(self, layer: str, purpose: str, points: List[Tuple[int, int]], widths: List[int], spaces: List[int], style0: int, style1: int, stylem: int, commit: bool) -> PyPath: ... - def add_pin(self, layer: str, net: str, label: str, bbox: BBox) -> None: ... - def add_pin_arr(self, net: str, label: str, tid: PyTrackID, lower: int, upper: int) -> None: ... - def add_poly(self, layer: str, purpose: str, points: List[Tuple[int, int]], commit: bool) -> PyPolygon: ... - def add_prim_instance(self, lib: str, cell: str, view: str, name: str, xform: Transform, nx: int, ny: int, spx: int, spy: int, commit: bool) -> PyLayInstRef: ... - def add_rect(self, layer: str, purpose: str, bbox: BBox, commit: bool) -> PyRect: ... - @overload - def add_rect_arr(self, layer: str, purpose: str, box: BBox, nx: int, ny: int, spx: int, spy: int) -> None: ... - @overload - def add_rect_arr(self, layer: str, purpose: str, barr: BBoxArray) -> None: ... - def add_rect_list(self, layer: str, purpose: str, bcol: BBoxCollection) -> None: ... - def add_via(self, xform: Transform, via_id: str, params: ViaParam, add_layers: bool, commit: bool) -> PyVia: ... - def add_via_arr(self, xform: Transform, via_id: str, params: ViaParam, add_layers: bool, nx: int, ny: int, spx: int, spy: int) -> None: ... - def add_via_on_intersections(self, tid1: PyTrackID, tid2: PyTrackID, l1: int, u1: int, l2: int, u2: int, extend: bool, contain: bool) -> Tuple[Tuple[int, int], Tuple[int, int]]: ... - def add_warr(self, tid: PyTrackID, lower: int, upper: int, is_dummy: bool = False) -> None: ... - def connect_barr_to_tracks(self, lev_code: int, layer: str, purpose: str, barr: BBoxArray, tid: PyTrackID, tr_lower: Optional[int], tr_upper: Optional[int], min_len_code: int, w_lower: Optional[int], w_upper: Optional[int]) -> Tuple[Tuple[int, int], Tuple[int, int]]: ... - def connect_warr_to_tracks(self, w_tid: PyTrackID, tid: PyTrackID, w_lower: int, w_upper: int) -> Tuple[Tuple[int, int], Tuple[int, int]]: ... - def do_max_space_fill(self, level: int, bbox: BBox, fill_boundary: bool, fill_info: Tuple[int, int, int, int, float]) -> None: ... - def get_intersect(self, layer: int, bnd_box: BBox, spx: int, spy: int, no_sp: bool) -> List[BBox]: ... - def get_rect_bbox(self, layer: str, purpose: str) -> BBox: ... - def set_grid(self, grid: PyRoutingGrid) -> None: ... - - -class PyLayInstRef: - @property - def committed(self) -> bool: ... - @property - def inst_name(self) -> str: ... - @property - def nx(self) -> int: ... - @nx.setter - def nx(self, val: int) -> None: ... - @property - def ny(self) -> int: ... - @ny.setter - def ny(self, val: int) -> None: ... - @property - def spx(self) -> int: ... - @spx.setter - def spx(self, val: int) -> None: ... - @property - def spy(self) -> int: ... - @spy.setter - def spy(self, val: int) -> None: ... - @property - def xform(self) -> Transform: ... - def __init__(self, *args: Any, **kwargs: Any) -> Any: ... - def commit(self) -> None: ... - def move_by(self, dx: int, dy: int) -> None: ... - def set_master(self, new_master: PyLayCellView) -> None: ... - def transform(self, xform: Transform) -> None: ... - - -class PyOADatabase: - def __init__(self, lib_def_fname: str) -> None: ... - def add_primitive_lib(self, lib_name: str) -> None: ... - def add_yaml_path(self, lib_name: str, yaml_path: str) -> None: ... - def create_lib(self, lib_name: str, lib_path: str, tech_lib: str) -> None: ... - def get_cells_in_lib(self, lib_name: str) -> List[str]: ... - def get_lib_path(self, lib_name: str) -> str: ... - def implement_lay_list(self, lib_name: str, view: str, cv_list: Iterable[Tuple[str, PyLayCellView]]) -> None: ... - def implement_sch_list(self, lib_name: str, sch_view: str, sym_view: str, cv_list: Iterable[Tuple[str, Tuple[PySchCellView, str]]]) -> None: ... - def import_gds(self, gds_fname: str, lib_name: str, layer_map: str, obj_map: str, grid: PyRoutingGrid, colors: TrackColoring) -> None: ... - def is_primitive_lib(self, lib_name: str) -> None: ... - def read_library(self, lib_name: str, view_name: str) -> List[Tuple[str, str]]: ... - def read_sch_recursive(self, lib_name: str, cell_name: str, view_name: str) -> List[Tuple[str, str]]: ... - - -class PyPath: - def __init__(self) -> None: ... - def commit(self) -> None: ... - - -class PyPolygon: - def __init__(self) -> None: ... - def commit(self) -> None: ... - - -class PyPolygon45: - def __init__(self) -> None: ... - def commit(self) -> None: ... - - -class PyPolygon90: - def __init__(self) -> None: ... - def commit(self) -> None: ... - - -class PyRect: - def __init__(self) -> None: ... - def commit(self) -> None: ... - - -class PyRoutingGrid: - @property - def bot_layer(self) -> int: ... - @property - def layout_unit(self) -> float: ... - @property - def resolution(self) -> float: ... - @property - def top_ignore_layer(self) -> int: ... - @property - def top_layer(self) -> int: ... - @property - def top_private_layer(self) -> int: ... - @overload - def __init__(self, tech: PyTech, fname: str) -> None: ... - @overload - def __init__(self, grid: PyRoutingGrid) -> None: ... - def __eq__(self, other: PyRoutingGrid) -> bool: ... - def __hash__(self) -> int: ... - def coord_to_htr(self, layer_id: int, coord: int, round_mode: int, even: bool) -> int: ... - def find_next_htr(self, layer_id: int, coord: int, ntr: int, round_mode: int, even: bool) -> int: ... - def get_block_size(self, layer_id: int, include_private: bool = False, half_blk_x: bool = False, half_blk_y: bool = False) -> Tuple[int, int]: ... - def get_copy_with(self, top_ignore_lay: int, top_private_lay: int, tr_specs: List[Tuple[int, int, int, int, int]]) -> PyRoutingGrid: ... - def get_direction(self, lay_id: int) -> Orient2D: ... - def get_line_end_sep_htr(self, lev_code: int, lay_id: int, ntr: int, adj_ntr: int) -> int: ... - def get_line_end_space(self, lay_id: int, num_tr: int, even: bool = False) -> int: ... - def get_min_cont_length(self, lay_id: int, num_tr: int) -> int: ... - def get_min_space(self, lay_id: int, num_tr: int, same_color: bool = False, even: bool = False) -> int: ... - def get_min_track_width(self, layer_id: int, idc: float = 0, iac_rms: float = 0, iac_peak: float = 0, length: int = -1, bot_ntr: int = 0, top_ntr: int = 0, dc_temp: int = -1000, rms_dt: int = -1000) -> int: ... - def get_next_length(self, lay_id: int, num_tr: int, cur_len: int, even: bool = False) -> int: ... - def get_prev_length(self, lay_id: int, num_tr: int, cur_len: int, even: bool = False) -> int: ... - def get_sep_htr(self, lay_id: int, ntr1: int, ntr2: int, same_color: bool) -> int: ... - def get_size_pitch(self, layer_id: int) -> Tuple[int, int]: ... - def get_space(self, lay_id: int, num_tr: int, same_color: bool = False, even: bool = False) -> int: ... - def get_track_coloring_at(self, tr_colors: TrackColoring, xform: Transform, child: PyRoutingGrid, top_layer: int) -> TrackColoring: ... - def get_track_info(self, lay_id: int) -> TrackInfo: ... - def get_track_offset(self, lay_id: int) -> int: ... - def get_track_pitch(self, lay_id: int) -> int: ... - def get_via_extensions(self, lev_code: int, lay_id: int, num_tr: int, adj_num_tr: int) -> Tuple[int, int]: ... - def get_via_extensions_dim(self, lev_code: int, lay_id: int, dim: int, adj_dim: int) -> Tuple[int, int]: ... - def get_via_extensions_dim_tr(self, lev_code: int, lay_id: int, dim: int, adj_num_tr: int) -> Tuple[int, int]: ... - def get_wire_bounds_htr(self, layer_id: int, htr: int, ntr: int) -> Tuple[int, int]: ... - def get_wire_em_specs(self, layer_id: int, num_tr: int, length: int = -1, vertical: bool = False, dc_temp: int = -1000, rms_dt: int = -1000) -> Tuple[float, float, float]: ... - def get_wire_total_width(self, layer_id: int, ntr: int) -> int: ... - def htr_to_coord(self, layer_id: int, htr: int) -> int: ... - def size_defined(self, layer_id: int) -> bool: ... - def transform_htr(self, layer_id: int, htr: int, xform: Transform) -> int: ... - - -class PySchCellView: - @property - def cell_name(self) -> str: ... - @cell_name.setter - def cell_name(self, val: str) -> None: ... - @property - def lib_name(self) -> str: ... - @lib_name.setter - def lib_name(self, val: str) -> None: ... - @property - def view_name(self) -> str: ... - def __init__(self, yaml_fname: str, sym_view: str = '') -> None: ... - def add_pin(self, new_name: str, term_type: int, sig_type: int, is_symbol: bool = False) -> None: ... - def array_instance(self, old_name: str, dx: int, dy: int, name_conn_range: Iterable[Tuple[str, Iterable[Tuple[str, str]]]]) -> None: ... - def clear_params(self) -> None: ... - def get_copy(self) -> PySchCellView: ... - def get_inst_ref(self, name: str) -> Optional[PySchInstRef]: ... - def get_signal_type(self, term: str) -> SigType: ... - def has_terminal(self, term: str) -> bool: ... - def inst_refs(self) -> Iterator[Tuple[str, PySchInstRef]]: ... - def remove_instance(self, name: str) -> bool: ... - def remove_pin(self, name: str, is_symbol: bool = False) -> bool: ... - def rename_instance(self, old_name: str, new_name: str) -> None: ... - def rename_pin(self, old_name: str, new_name: str, is_symbol: bool = False) -> None: ... - def set_param(self, name: str, val: Union[int, float, bool, str]) -> None: ... - def set_pin_attribute(self, pin_name: str, key: str, val: str) -> None: ... - def terminals(self) -> Iterator[Tuple[str, int]]: ... - def to_yaml(self) -> str: ... - - -class PySchCellViewInfo: - @property - def cell_name(self) -> str: ... - @property - def lib_name(self) -> str: ... - def __init__(self, yaml_fname: str) -> None: ... - def to_file(self, yaml_fname: str) -> None: ... - - -class PySchInstRef: - @property - def cell_name(self) -> str: ... - @cell_name.setter - def cell_name(self, val: str) -> None: ... - @property - def height(self) -> int: ... - @property - def is_primitive(self) -> bool: ... - @is_primitive.setter - def is_primitive(self, val: bool) -> None: ... - @property - def lib_name(self) -> str: ... - @lib_name.setter - def lib_name(self, val: str) -> None: ... - @property - def width(self) -> int: ... - def __init__(self, *args: Any, **kwargs: Any) -> Any: ... - def check_connections(self, pin_iter: Iterable[str]) -> None: ... - def get_connection(self, term_name: str) -> str: ... - def set_param(self, name: str, val: Union[int, float, bool, str]) -> None: ... - def update_connection(self, inst_name: str, term: str, net: str) -> None: ... - def update_master(self, lib: str, cell: str, prim: bool, keep_connections: bool) -> None: ... - - -class PyTech: - @property - def bot_layer(self) -> int: ... - @property - def default_purpose(self) -> str: ... - @property - def label_purpose(self) -> str: ... - @property - def layout_unit(self) -> float: ... - @property - def make_pin(self) -> bool: ... - @property - def pin_purpose(self) -> str: ... - @property - def resolution(self) -> float: ... - @property - def tech_lib(self) -> str: ... - @property - def use_track_coloring(self) -> bool: ... - def __init__(self, tech_fname: str) -> None: ... - def get_lay_purp_list(self, layer_id: int, is_dummy: bool = False) -> List[Tuple[str, str]]: ... - def get_layer_id(self, layer: str, purpose: str = '') -> Optional[int]: ... - def get_metal_em_specs(self, layer: str, purpose: str, width: int, length: int = -1, vertical: bool = False, dc_temp: int = -1000, rms_dt: int = -1000) -> Tuple[float, float, float]: ... - def get_min_line_end_space(self, layer: str, width: int, purpose: str = '', even: bool = False) -> int: ... - def get_min_space(self, layer: str, width: int, purpose: str = '', same_color: bool = False, even: bool = False) -> int: ... - def get_next_length(self, layer: str, purpose: str, tr_dir_code: int, width: int, cur_len: int, even: bool = False) -> int: ... - def get_prev_length(self, layer: str, purpose: str, tr_dir_code: int, width: int, cur_len: int, even: bool = False) -> int: ... - def get_via_em_specs(self, layer_dir: int, layer: str, purpose: str, adj_layer: str, adj_purpose: str, cut_w: int, cut_h: int, m_w: int = -1, m_l: int = -1, adj_m_w: int = -1, adj_m_l: int = -1, array: bool = False, dc_temp: int = -1000, rms_dt: int = -1000) -> Tuple[float, float, float]: ... - def get_via_id(self, lev_code: int, lay: str, purp: str, adj_lay: str, adj_purp: str) -> str: ... - def get_via_param(self, w: int, h: int, via_id: str, lev_code: int, ex_dir: int, adj_ex_dir: int, extend: bool) -> ViaParam: ... - - -class PyTrackID: - @property - def base_htr(self) -> int: ... - @base_htr.setter - def base_htr(self, val: int) -> None: ... - @property - def htr_pitch(self) -> int: ... - @htr_pitch.setter - def htr_pitch(self, val: int) -> None: ... - @property - def layer_id(self) -> int: ... - @property - def num(self) -> int: ... - @property - def width(self) -> int: ... - def __init__(self, layer_id: int, htr: int, ntr: int, num: int, htr_pitch: int) -> None: ... - def __eq__(self, other: PyTrackID) -> bool: ... - def __hash__(self) -> int: ... - def get_bounds(self, grid: PyRoutingGrid) -> Tuple[int, int]: ... - - -class PyVia: - @property - def bottom_box(self) -> BBox: ... - @property - def top_box(self) -> BBox: ... - @property - def via_cuts(self) -> List[BBox]: ... - def __init__(self) -> None: ... - def commit(self) -> None: ... - - -class RTree: - @property - def bound_box(self) -> BBox: ... - def __init__(self) -> None: ... - def __bool__(self) -> bool: ... - def __getitem__(self, obj_id: int) -> object: ... - def __iter__(self) -> Iterator[Tuple[BBox, int]]: ... - def insert(self, obj: object, box: BBox) -> int: ... - def intersect_iter(self, box: BBox) -> Iterator[Tuple[BBox, int]]: ... - def overlap_iter(self, box: BBox) -> Iterator[Tuple[BBox, int]]: ... - def pop(self, obj_id: int) -> object: ... - - -class TrackColoring: - def __init__(self, *args: Any, **kwargs: Any) -> Any: ... - def __eq__(self, other: TrackColoring) -> bool: ... - def __hash__(self) -> int: ... - def get_htr_parity(self, level: int, htr: int) -> int: ... - - -class TrackInfo: - @property - def offset(self) -> int: ... - @property - def pitch(self) -> int: ... - @property - def space(self) -> int: ... - @property - def width(self) -> int: ... - def __init__(self, *args: Any, **kwargs: Any) -> Any: ... - def __eq__(self, other: TrackInfo) -> bool: ... - def __hash__(self) -> int: ... - def compatible(self, other: TrackInfo) -> bool: ... - - -class Transform: - @property - def axis_scale(self) -> Tuple[int, int]: ... - @property - def flips_xy(self) -> bool: ... - @property - def location(self) -> Tuple[int, int]: ... - @property - def orient(self) -> Orientation: ... - @property - def x(self) -> int: ... - @property - def y(self) -> int: ... - def __init__(self, dx: int = 0, dy: int = 0, mode: int = 0) -> None: ... - def get_inverse(self) -> Transform: ... - def get_move_by(self, dx: int = 0, dy: int = 0) -> Transform: ... - def get_transform_by(self, xform: Transform) -> Transform: ... - def invert(self) -> Transform: ... - def move_by(self, dx: int = 0, dy: int = 0) -> Transform: ... - def transform_by(self, xform: Transform) -> Transform: ... - - -class ViaParam: - @property - def cut_dim(self) -> Tuple[int, int]: ... - @property - def empty(self) -> bool: ... - @property - def nx(self) -> int: ... - @property - def ny(self) -> int: ... - @property - def priority(self) -> int: ... - def __init__(self, vnx: int, vny: int, w: int, h: int, vspx: int, vspy: int, enc1l: int, enc1r: int, enc1t: int, enc1b: int, enc2l: int, enc2r: int, enc2t: int, enc2b: int, priority: int) -> None: ... - def get_box(self, xform: Transform, level: int) -> BBox: ... - - diff --git a/src/pybag/enum.py b/src/pybag/enum.py index 6802a83..709e8ef 100644 --- a/src/pybag/enum.py +++ b/src/pybag/enum.py @@ -58,6 +58,7 @@ class DesignOutput(IntEnum): SYSVERILOG = 6 SPECTRE = 7 OASIS = 8 + NGSPICE = 9 @property def extension(self) -> str: @@ -75,6 +76,8 @@ def extension(self) -> str: return 'scs' elif self is DesignOutput.OASIS: return 'oasis' + elif self is DesignOutput.NGSPICE: + return 'cir' else: raise ValueError(f'Unsupported output type: {self.name}') @@ -84,7 +87,7 @@ def is_model(self) -> bool: @property def is_netlist(self) -> bool: - return self is DesignOutput.CDL or self is DesignOutput.SPECTRE + return self is DesignOutput.CDL or self is DesignOutput.SPECTRE or self is DesignOutput.NGSPICE @property def fallback_model_type(self) -> DesignOutput: diff --git a/src/pybag/name.cpp b/src/pybag/name.cpp index 9edaa19..c11afd7 100644 --- a/src/pybag/name.cpp +++ b/src/pybag/name.cpp @@ -97,6 +97,8 @@ pyg::List get_cdba_name_bits(const std::string &name, return _get_name_bits_helper(name); case cbag::design_output::SPECTRE: return _get_name_bits_helper(name); + case cbag::design_output::NGSPICE: + return _get_name_bits_helper(name); default: throw std::invalid_argument("Unknown design output code: " + std::to_string(design_output_code)); @@ -118,6 +120,8 @@ py::str convert_cdba_name_bit(const std::string &name, return _convert_name_bit_helper(name); case cbag::design_output::SPECTRE: return _convert_name_bit_helper(name); + case cbag::design_output::NGSPICE: + return _convert_name_bit_helper(name); default: throw std::invalid_argument("Unknown design output code: " + std::to_string(design_output_code)); diff --git a/src/pybag/schematic.cpp b/src/pybag/schematic.cpp index 37e00f8..b0fe847 100644 --- a/src/pybag/schematic.cpp +++ b/src/pybag/schematic.cpp @@ -150,6 +150,68 @@ class const_term_iterator { } }; +class const_conn_iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = std::pair; + using difference_type = cbag::util::sorted_map::const_iterator::difference_type; + using pointer = const value_type *; + using reference = const value_type &; + + private: + cbag::util::sorted_map::const_iterator iter_; + + public: + const_conn_iterator() = default; + const_conn_iterator(cbag::util::sorted_map::const_iterator val) : iter_(std::move(val)) {} + + bool operator==(const const_conn_iterator &other) const { return iter_ == other.iter_; } + bool operator!=(const const_conn_iterator &other) const { return iter_ != other.iter_; } + + value_type operator*() const { return {iter_->first, iter_->second}; } + + const_conn_iterator &operator++() { + ++iter_; + return *this; + } + const_conn_iterator operator++(int) { + const_conn_iterator ans(iter_); + operator++(); + return ans; + } +}; + +class const_param_iterator { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = std::pair; + using difference_type = cbag::param_map::const_iterator::difference_type; + using pointer = const value_type *; + using reference = const value_type &; + + private: + cbag::param_map::const_iterator iter_; + + public: + const_param_iterator() = default; + const_param_iterator(cbag::param_map::const_iterator val) : iter_(std::move(val)) {} + + bool operator==(const const_param_iterator &other) const { return iter_ == other.iter_; } + bool operator!=(const const_param_iterator &other) const { return iter_ != other.iter_; } + + value_type operator*() const { return {iter_->first, iter_->second}; } + + const_param_iterator &operator++() { + ++iter_; + return *this; + } + const_param_iterator operator++(int) { + const_param_iterator ans(iter_); + operator++(); + return ans; + } +}; + // python/C++ interface functions for cellview pyg::PyIterator> inst_ref_iter(const c_cellview &cv) { return pyg::make_iterator(const_inst_iterator(cv.instances.begin()), @@ -161,6 +223,16 @@ pyg::PyIterator> terminals_iter(const c_cellview &cv const_term_iterator(cv.terminals.end())); } +pyg::PyIterator> connections_iter(const c_instance &inst) { + return pyg::make_iterator(const_conn_iterator(inst.connections.begin()), + const_conn_iterator(inst.connections.end())); +} + +pyg::PyIterator> params_iter(const c_instance &inst) { + return pyg::make_iterator(const_param_iterator(inst.params.begin()), + const_param_iterator(inst.params.end())); +} + bool has_terminal(const c_cellview &cv, const std::string &term) { return cv.terminals.find(term) != cv.terminals.end(); } @@ -238,9 +310,11 @@ void implement_netlist( cbag::sch::cellview_info(*cv_info_ptr)); } - // don't add BAG_prim in dut and harness spectre netlists; only testbench netlist will define those + // don't add BAG_prim in dut and harness simulation netlists; only testbench netlist will define those if ((format == cbag::design_output::SPECTRE) && top_subckt) append_file.clear(); + else if ((format == cbag::design_output::NGSPICE) && top_subckt) + append_file.clear(); if (cv_netlist_list.size() != 0) { inc_list.clear(); @@ -319,6 +393,11 @@ void bind_schematic(py::module &m) { "Check instance connections are valid", py::arg("pin_iter")); py_inst.def("get_connection", pysch::get_connection, "Get net connected to the given terminal.", py::arg("term_name")); + pyg::declare_iterator(); + py_inst.def("connections", &pysch::connections_iter, + "Returns an iterator over all (terminal, net) tuples."); + pyg::declare_iterator(); + py_inst.def("params", &pysch::params_iter, "Returns an iterator over all parameters."); auto py_info = py::class_(m, "PySchCellViewInfo"); py_info.doc() = "An information object describing a schematic master instance."; diff --git a/tests/test_imports.py b/tests/test_imports.py new file mode 100644 index 0000000..add9664 --- /dev/null +++ b/tests/test_imports.py @@ -0,0 +1,11 @@ +# Tests if we can import pybag correctly. + +# TODO: in pytest 7, this can be moved to pytest.ini +import sys +sys.path.append('_build/lib') + +from pybag.enum import * +from pybag.core import * + +def test_import(): + assert True diff --git a/tests/test_pytest.py b/tests/test_pytest.py new file mode 100644 index 0000000..bf210df --- /dev/null +++ b/tests/test_pytest.py @@ -0,0 +1,9 @@ +# Sanity checks that we can run pytest. + +def test_always_passes(): + # Should always pass + assert True + +# def test_always_fails(): +# # if enabled, should always fail +# assert False