diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..7bcaef7 --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +extend-exclude = third_party,setup/deps,_skbuild +max-line-length = 100 +ignore = diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..102e79a --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,42 @@ +name: 'CI Tests' + +on: + workflow_dispatch: + push: + +jobs: + python_test_job: + timeout-minutes: 15 + runs-on: ${{ matrix.version.os }} + name: 'Pure Python tests' + strategy: + fail-fast: false + matrix: + version: + - {python: "3.6", os: "ubuntu-20.04"} + - {python: "3.7", os: "ubuntu-latest"} + - {python: "3.8", os: "ubuntu-latest"} + - {python: "3.9", os: "ubuntu-latest"} + - {python: "3.10", os: "ubuntu-latest"} + - {python: "3.11", os: "ubuntu-latest"} + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install graphviz + + - name: Set up Python ${{ matrix.version.python }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.version.python }} + cache: pip + + - name: Run Python tests + run: | + python3 -m pip install --upgrade pip + python3 -m pip install -e .[test] + pytest diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml new file mode 100644 index 0000000..891bcbd --- /dev/null +++ b/.github/workflows/wheels.yml @@ -0,0 +1,135 @@ +name: Wheels + +on: + workflow_dispatch: + release: + types: + - published + +# Ensures wheels are compatible with macOS 10.15+ +env: + MACOSX_DEPLOYMENT_TARGET: "10.15" + +jobs: + build_wheels: + name: Wheels leflib on ${{ matrix.platform.os }} ${{ matrix.platform.arch}} + runs-on: ${{ matrix.platform.os }} + strategy: + fail-fast: false + matrix: + platform: + - os: ubuntu-latest + arch: x86_64 + - os: ubuntu-latest + arch: aarch64 + - os: macos-latest + arch: universal + - os: windows-latest + arch: x86_64 + + env: + CIBW_ARCHS_LINUX: ${{ matrix.platform.arch }} + + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + # This facilitates building Linux+arm64 wheels + # https://cibuildwheel.readthedocs.io/en/stable/faq/#emulation + - name: Set up QEMU + if: runner.os == 'Linux' + uses: docker/setup-qemu-action@v2 + with: + platforms: all + + - name: Setup env (Windows) + if: matrix.platform.os == 'windows-latest' + run: | + choco install -y winflexbison3 + vcpkg install zlib zlib:x64-windows + + - name: Setup env (macOS) + if: matrix.platform.os == 'macos-latest' + run: | + brew install bison + # https://github.com/The-OpenROAD-Project/OpenROAD/issues/1688 + echo "/usr/local/opt/bison/bin" >> $GITHUB_PATH + brew install flex + echo "/usr/local/opt/flex/bin" >> $GITHUB_PATH + + - uses: pypa/cibuildwheel@v2.12.1 + env: + CIBW_BEFORE_ALL_LINUX: | + yum --disablerepo=epel -y update ca-certificates + yum install -y zlib-devel wget + CIBW_BEFORE_BUILD_WINDOWS: if exist "{package}\\_skbuild\" rd /q /s "{package}\\_skbuild" + CIBW_ENVIRONMENT_MACOS: > + LDFLAGS="-L/usr/local/opt/bison/lib -L/usr/local/opt/flex/lib" + CPPFLAGS="-I/usr/local/opt/flex/include" + CIBW_ENVIRONMENT_WINDOWS: SC_CMAKEARGS="-DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake." + CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 + CIBW_SKIP: "pp* *win32 *i686 *-musllinux_*" + CIBW_ARCHS_MACOS: x86_64 arm64 + CIBW_TEST_SKIP: "*_arm64" + CIBW_TEST_EXTRAS: test + CIBW_TEST_COMMAND: > + pytest {package}/tests/ + + # "if: always()" ensures that we always upload any wheels that have + # been created, even if cibuildwheel action fails + - name: Upload wheels + if: always() + uses: actions/upload-artifact@v3 + with: + name: artifact + path: wheelhouse/*.whl + + publish: + needs: [build_wheels] + runs-on: ubuntu-latest + if: github.event_name == 'release' && github.event.action == 'published' && !contains(github.event.release.body, 'NOPUBLISH') + + steps: + - uses: actions/download-artifact@v3 + with: + name: artifact + path: dist + + - uses: pypa/gh-action-pypi-publish@v1.4.2 + with: + user: __token__ + password: ${{ secrets.PYPI_DEPLOY }} + + package_offline: + # We want to run this on the official PEP Python-wheel building platform to + # ensure the downloaded wheels have the broadest compatibility. Using the + # '--platform' tag for 'pip download' doesn't work for us, since it requires + # setting --only-binary=:all: and some of our deps aren't available as + # wheels on some platforms. + container: quay.io/pypa/manylinux2014_x86_64 + needs: [build_wheels] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python: [cp36-cp36m, cp37-cp37m, cp38-cp38, cp39-cp39, cp310-cp310, cp311-cp311] + steps: + - uses: actions/download-artifact@v3 + with: + name: artifact + path: dist + + - name: Package sc-leflib + run: | + mkdir deps + $python -m pip download pip -d deps + $python -m pip download ./dist/sc_leflib*${{matrix.python}}*linux*x86_64.whl -d deps + tar -czvf deps-${{matrix.python}}.tar.gz deps + env: + python: /opt/python/${{matrix.python}}/bin/python + + - name: Upload package + uses: actions/upload-artifact@v3 + with: + path: deps*.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..861a219 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ + +# Byte-compiled / optimized / DLL files +__pycache__/ + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +wheels/ +share/python-wheels/ +*.egg-info/ +*.egg +MANIFEST +.vagrant/ + +# Scikit-build build directory +_skbuild/ + +# C++ module for leflib gets installed in leflib/ after running pip install -e . +_leflib.*.so diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8db9747 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "siliconcompiler"] + path = siliconcompiler + url = ../siliconcompiler diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..73a2350 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,80 @@ +cmake_minimum_required(VERSION 3.15...3.19) + +project(sc-leflib) + +# Need C++11 support +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Packages we rely on +find_package(PythonInterp) +find_package(PythonLibs) +find_package(PythonExtensions) +find_package(Cython REQUIRED) + +set(TOOLS_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/siliconcompiler/setup/_tools.py) + +execute_process( + COMMAND python3 ${TOOLS_SCRIPT} --tool openroad --field git-url + OUTPUT_VARIABLE openroad_URL +) +string(STRIP ${openroad_URL} openroad_URL) +execute_process( + COMMAND python3 ${TOOLS_SCRIPT} --tool openroad --field git-commit + OUTPUT_VARIABLE openroad_COMMIT +) +string(STRIP ${openroad_COMMIT} openroad_COMMIT) + +include(FetchContent) +FetchContent_Declare(openroad + GIT_REPOSITORY ${openroad_URL} + GIT_TAG ${openroad_COMMIT} + CONFIGURE_COMMAND "" + BUILD_COMMAND "" +) + +# Needed to find *.pxd file +include_directories(${CMAKE_CURRENT_LIST_DIR}/sc_leflib) + +# Build Cython module +# source: https://github.com/scikit-build/scikit-build-sample-projects/blob/master/projects/hello-cython/hello/CMakeLists.txt +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/sc_leflib) +add_cython_target(_leflib ${CMAKE_CURRENT_LIST_DIR}/sc_leflib/_leflib.pyx CXX) +add_library(_leflib MODULE ${_leflib}) +python_extension_module(_leflib) + +# Link in lef library +target_link_libraries(_leflib lef) + +# We want to be extra strict about error checking -- enable all warnings and +# treat them as errors. We only apply this to the _leflib target since the LEF +# parser library has warnings under -Wall -pedantic, but there's not much we can +# do about those. +# +# We exempt deprecated-declarations since Cython uses something deprecated in at +# least one of the Python versions we support, and there's not much we can do +# about that either. +# +# Microsoft's compiler uses a totally different set of flags, so we just set +# these for MacOS/Linux (which will be using GCC or clang). +if(NOT WIN32) + target_compile_options(_leflib PRIVATE -Wall -Werror -Wno-error=deprecated-declarations) +endif() + +FetchContent_Populate(openroad) +# Stuff to include Si2 LEF library +set(LEF_DIR ${openroad_SOURCE_DIR}/src/odb/src/lef) + +# this lets us include lef headers +include_directories(${LEF_DIR}/lef) +# this causes cmake to build lef/ +add_subdirectory(${LEF_DIR}) + +# The CMake config for the LEF parser sets a single compilation option, +# -Wno-class-memaccess, which is incompatible with Microsoft's compiler. I don't +# see any particular reason to enable this warning, so overwrite the flags here +# to ensure our Windows build passes. +set_target_properties(lef PROPERTIES COMPILE_OPTIONS "") + +install(TARGETS _leflib DESTINATION .) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..60e162c --- /dev/null +++ b/LICENSE @@ -0,0 +1,190 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2020 Zero ASIC Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..e18e2d4 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,10 @@ +# These files should be included in the sdist by default, but we include them +# manually since scikit-build breaks this behavior: +# https://github.com/scikit-build/scikit-build/issues/525 +include pyproject.toml +include setup.py +include README.md + +include CMakeLists.txt + +graft sc_leflib diff --git a/README.md b/README.md new file mode 100644 index 0000000..aeeeae7 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +[![CI Tests](https://github.com/siliconcompiler/sc-leflib/actions/workflows/tests.yml/badge.svg)](https://github.com/siliconcompiler/sc-leflib/actions/workflows/tests.yml) +[![Wheels](https://github.com/siliconcompiler/sc-leflib/actions/workflows/wheels.yml/badge.svg?event=schedule)](https://github.com/siliconcompiler/sc-leflib/actions/workflows/wheels.yml) +[![Downloads](https://static.pepy.tech/personalized-badge/sc-leflib?period=total&units=international_system&left_color=grey&right_color=blue&left_text=Downloads)](https://pepy.tech/project/sc-leflib) + +# What is sc-leflib? + +A [LEF](https://en.wikipedia.org/w/index.php?title=Library_Exchange_Format&oldid=1105463237) parser for [SiliconCompiler](https://github.com/siliconcompiler/siliconcompiler) based on [OpenROADs](https://github.com/The-OpenROAD-Project/OpenROAD) implementation. + +# Contributing + +sc-leflib is an open-source project and welcomes contributions. + +# Issues / Bugs + +We use [GitHub Issues](https://github.com/siliconcompiler/sc-leflib/issues) +for tracking requests and bugs. + +# License + +- [Apache License 2.0](LICENSE) +- [LEF Parser / Apache License 2.0](https://github.com/The-OpenROAD-Project/OpenROAD/blob/master/src/odb/src/lef/LICENSE.TXT) + +# More information + +[Docs](https://docs.siliconcompiler.com/en/latest/reference_manual/floorplan_api.html) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9b091c6 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,14 @@ +[build-system] +requires = [ + "setuptools>=45,<64", + "wheel", + "cython", + "scikit-build>=0.12", + "cmake", + "setuptools_scm>=6.2" +] +build-backend = "setuptools.build_meta" + +[tool.pytest.ini_options] +testpaths = "tests" +timeout = "120" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3ff6ccd --- /dev/null +++ b/requirements.txt @@ -0,0 +1,11 @@ +# Build dependencies +#:build +scikit-build >= 0.14.1 +cython +cmake + +# Testing dependencies +#:test +pytest >= 6.2.4 +pytest-timeout >= 2.1.0 +flake8 >= 5.0.0 diff --git a/sc_leflib/CMakeLists.txt b/sc_leflib/CMakeLists.txt new file mode 100644 index 0000000..ac804a3 --- /dev/null +++ b/sc_leflib/CMakeLists.txt @@ -0,0 +1,2 @@ +# Need blank file here so CMake can process this directory and perform Cython +# build diff --git a/sc_leflib/__init__.py b/sc_leflib/__init__.py new file mode 100644 index 0000000..ab9cd97 --- /dev/null +++ b/sc_leflib/__init__.py @@ -0,0 +1,166 @@ +from ._leflib import parse as _parse + + +def parse(path): + ''' Parses LEF file. + + Given a path to a LEF file, this function parses the file and returns a + dictionary representing the contents of the LEF file. If there's an error + while reading or parsing the file, this function returns None instead. + + Note that this function does not return all information contained in the + LEF. The subset of information returned includes: + + * LEF version + * Bus bit characters + * Divider characters + * Units + * Manufacturing grid + * Use min spacing + * Clearance measure + * Fixed mask + * Layer information + + * Type + * Width + * Direction + * Offset + * Pitch + + * Max stack via + * Viarules + * Sites + * Macro information + + * Size + * Pins + * Obstructions + + The dictionary returned by this function is designed to mimic the structure + of the LEF file as closely as possible, and this function does minimal + legality checking. The order all top-level objects appear in the dictionary + is guaranteed to match the LEF file. It looks like follows: + + .. code-block:: python + + { + 'version': 5.8, + 'busbitchars': '<>', + 'dividerchar': ':', + 'units': { + 'capacitance': 10.0, + 'current': 10000.0, + 'database': 20000.0, + 'frequency': 10.0, + 'power': 10000.0, + 'resistance': 10000.0, + 'time': 100.0, + 'voltage': 1000.0 + }, + 'manufacturinggrid': 0.05, + 'useminspacing': {'OBS': 'OFF'}, + 'clearancemeasure': 'MAXXY', + 'fixedmask': True, + 'layers': { + 'M1': { + 'type': 'ROUTING', + 'direction': 'HORIZONTAL', + 'offset': (0.1, 0.2), + 'pitch': 1.8, + 'width': 1.0 + }, + 'V1': { + 'type': 'CUT', + }, + ... + }, + 'maxviastack': {'range': {'bottom': 'm1', 'top': 'm7'}, 'value': 4}, + 'viarules': { + '': { + 'generate': True, + 'layers': [ + {'enclosure': {'overhang1': 1.4, + 'overhang2': 1.5}, + 'name': 'M1', + 'width': {'max': 19.0, 'min': 0.1}}, + {'enclosure': {'overhang1': 1.4, + 'overhang2': 1.5}, + 'name': 'M2', + 'width': {'max': 1.9, 'min': 0.2}}, + {'name': 'M3', + 'rect': (-0.3, -0.3, -0.3, 0.3), + 'resistance': 0.5, + 'spacing': {'x': 5.6, 'y': 7.0}} + ] + }, + '': { + {'layers': [ + {'direction': 'VERTICAL', + 'name': 'M1', + 'width': {'max': 9.6, 'min': 9.0}}, + {'direction': 'HORZIONTAL', + 'name': 'M1', + 'width': {'max': 3.0, 'min': 3.0}} + ]} + }, + ... + } + 'macros': { + '': { + 'size': { + 'width': 5, + 'height': 8 + }, + 'pins': { + '': { + 'ports': [{ + 'class': 'CORE', + 'layer_geometries': [{ + 'layer': 'M1', + 'exceptpgnet': True, + 'spacing': 0.01, + 'designrulewidth': 0.05, + 'width': 1.5, + 'shapes': [ + { + 'rect': (0, 0, 5, 5), + 'mask': 1, + 'iterate': { + 'num_x': 2, + 'num_y': 3, + 'space_x': 1, + 'space_y': 4 + } + }, + { + 'path': [(0, 0), (5, 0), (0, 5)], + 'iterate': ... + }, + { + 'polygon': [(0, 0), (5, 0), (0, 5)], + 'iterate': ... + } + ], + 'via': { + 'pt': (2, 3), + 'name': 'via1', + 'iterate': ... + } + }] + }] + }, + ... + } + }, + ... + } + } + + If some entry is not specified in the LEF, the corresponding key will not be + present in the dictionary. + + Args: + path (str): Path to LEF file to parse. + ''' + + return _parse(path) diff --git a/sc_leflib/_leflib.pxd b/sc_leflib/_leflib.pxd new file mode 100644 index 0000000..c6b645f --- /dev/null +++ b/sc_leflib/_leflib.pxd @@ -0,0 +1,389 @@ +''' +This file contains declarations of all C/C++ types and functions defined in the +LEF parser that we need to interface with from Cython. + +We need to declare all these things here since Cython is unable to parse the +header files directly. However, note that we don't need to redeclare everything, +or each member of an object we want to use. Instead, we just need to declare +everything we actually use by name in Cython. + +In general, the contents of this file have been copy-pasted directly from the +LEF parser library (and cleaned up as needed). +''' + +from libc.stdio cimport FILE + +# lefiUserData used in each callback is just defined by the LEF parser as a +# `#define` macro for void*. +ctypedef void* lefiUserData + +# These typedefs define the function signatures of each LEF parser callback. +# (Order same as in lefrReader.hpp) +ctypedef int (*lefrVoidCbkFnType) (lefrCallbackType_e, + void* num, + lefiUserData); +ctypedef int (*lefrStringCbkFnType) (lefrCallbackType_e, + const char *string, + lefiUserData); +ctypedef int (*lefrIntegerCbkFnType) (lefrCallbackType_e, + int number, + lefiUserData); +ctypedef int (*lefrDoubleCbkFnType) (lefrCallbackType_e, + double number, + lefiUserData); +ctypedef int (*lefrUnitsCbkFnType) (lefrCallbackType_e, + lefiUnits* units, + lefiUserData); +ctypedef int (*lefrLayerCbkFnType) (lefrCallbackType_e, + lefiLayer* l, + lefiUserData); +ctypedef int (*lefrViaRuleCbkFnType) (lefrCallbackType_e, + lefiViaRule* l, + lefiUserData); +ctypedef int (*lefrSiteCbkFnType) (lefrCallbackType_e, + lefiSite* l, + lefiUserData); +ctypedef int (*lefrMacroCbkFnType) (lefrCallbackType_e, + lefiMacro* l, + lefiUserData); +ctypedef int (*lefrPinCbkFnType) (lefrCallbackType_e, + lefiPin* l, + lefiUserData); +ctypedef int (*lefrObstructionCbkFnType) (lefrCallbackType_e, + lefiObstruction* l, + lefiUserData); +ctypedef int (*lefrUseMinSpacingCbkFnType) (lefrCallbackType_e, + lefiUseMinSpacing* l, + lefiUserData); +ctypedef int (*lefrMaxStackViaCbkFnType) (lefrCallbackType_e, + lefiMaxStackVia* l, + lefiUserData); + +ctypedef void (*LEFI_LOG_FUNCTION) (const char*); +ctypedef void (*LEFI_WARNING_LOG_FUNCTION) (const char*); + +# Must declare everything we reference from lefrReader.hpp here +cdef extern from "lefrReader.hpp": + int lefrInit() + int lefrRead (FILE *file, const char *fileName, void* userData) + + # Callback setters + void lefrSetVersionCbk(lefrDoubleCbkFnType) + void lefrSetBusBitCharsCbk(lefrStringCbkFnType) + void lefrSetDividerCharCbk(lefrStringCbkFnType) + void lefrSetUnitsCbk(lefrUnitsCbkFnType) + void lefrSetManufacturingCbk(lefrDoubleCbkFnType) + void lefrSetUseMinSpacingCbk(lefrUseMinSpacingCbkFnType) + void lefrSetClearanceMeasureCbk(lefrStringCbkFnType) + void lefrSetFixedMaskCbk(lefrIntegerCbkFnType) + void lefrSetLayerCbk(lefrLayerCbkFnType) + void lefrSetMaxStackViaCbk(lefrMaxStackViaCbkFnType) + void lefrSetViaRuleCbk(lefrViaRuleCbkFnType) + void lefrSetSiteCbk(lefrSiteCbkFnType) + void lefrSetMacroBeginCbk(lefrStringCbkFnType) + void lefrSetPinCbk(lefrPinCbkFnType) + void lefrSetObstructionCbk(lefrObstructionCbkFnType) + void lefrSetMacroCbk(lefrMacroCbkFnType) + + # Additional callbacks + void lefrSetLogFunction(LEFI_LOG_FUNCTION) + void lefrSetWarningLogFunction(LEFI_WARNING_LOG_FUNCTION) + + # Some enums we need to reference + ctypedef enum lefrCallbackType_e: + lefrUnspecifiedCbkType, + lefrVersionCbkType, + lefrVersionStrCbkType, + lefrDividerCharCbkType, + lefrBusBitCharsCbkType, + lefrUnitsCbkType, + lefrCaseSensitiveCbkType, + lefrNoWireExtensionCbkType, + lefrPropBeginCbkType, + lefrPropCbkType, + lefrPropEndCbkType, + lefrLayerCbkType, + lefrViaCbkType, + lefrViaRuleCbkType, + lefrSpacingCbkType, + lefrIRDropCbkType, + lefrDielectricCbkType, + lefrMinFeatureCbkType, + lefrNonDefaultCbkType, + lefrSiteCbkType, + lefrMacroBeginCbkType, + lefrPinCbkType, + lefrMacroCbkType, + lefrObstructionCbkType, + lefrArrayCbkType, + lefrSpacingBeginCbkType, + lefrSpacingEndCbkType, + lefrArrayBeginCbkType, + lefrArrayEndCbkType, + lefrIRDropBeginCbkType, + lefrIRDropEndCbkType, + lefrNoiseMarginCbkType, + lefrEdgeRateThreshold1CbkType, + lefrEdgeRateThreshold2CbkType, + lefrEdgeRateScaleFactorCbkType, + lefrNoiseTableCbkType, + lefrCorrectionTableCbkType, + lefrInputAntennaCbkType, + lefrOutputAntennaCbkType, + lefrInoutAntennaCbkType, + lefrAntennaInputCbkType, + lefrAntennaInoutCbkType, + lefrAntennaOutputCbkType, + lefrManufacturingCbkType, + lefrUseMinSpacingCbkType, + lefrClearanceMeasureCbkType, + lefrTimingCbkType, + lefrMacroClassTypeCbkType, + lefrMacroOriginCbkType, + lefrMacroSizeCbkType, + lefrMacroFixedMaskCbkType, + lefrMacroEndCbkType, + lefrMaxStackViaCbkType, + lefrExtensionCbkType, + lefrDensityCbkType, + lefrFixedMaskCbkType, + lefrMacroSiteCbkType, + lefrMacroForeignCbkType, + lefrLibraryEndCbkType + + cdef enum lefiGeomEnum: + lefiGeomUnknown, + lefiGeomLayerE, + lefiGeomLayerExceptPgNetE, + lefiGeomLayerMinSpacingE, + lefiGeomLayerRuleWidthE, + lefiGeomWidthE, + lefiGeomPathE, + lefiGeomPathIterE, + lefiGeomRectE, + lefiGeomRectIterE, + lefiGeomPolygonE, + lefiGeomPolygonIterE, + lefiGeomViaE, + lefiGeomViaIterE, + lefiGeomClassE, + lefiGeomEnd + + # Objects passed into callbacks + cdef cppclass lefiUnits: + int hasDatabase() + int hasCapacitance() + int hasResistance() + int hasTime() + int hasPower() + int hasCurrent() + int hasVoltage() + int hasFrequency() + double databaseNumber() + double capacitance() + double resistance() + double time() + double power() + double current() + double voltage() + double frequency() + + cdef cppclass lefiUseMinSpacing: + const char* name() + int value() + + cdef cppclass lefiLayer: + int hasType() + int hasMask() + int hasPitch() + int hasXYPitch() + int hasOffset() + int hasXYOffset() + int hasWidth() + int hasArea() + int hasDirection() + + char* name() + const char* type() + double pitch() + int mask() + double pitchX() + double pitchY() + double offset() + double offsetX() + double offsetY() + double width() + double area() + const char* direction() + + cdef cppclass lefiMaxStackVia: + int maxStackVia() + int hasMaxStackViaRange() + const char* maxStackViaBottomLayer() + const char* maxStackViaTopLayer() + + cdef cppclass lefiViaRule: + int hasGenerate() + int hasDefault() + char* name() + + int numLayers() + lefiViaRuleLayer* layer(int index) + + int numVias() + char* viaName(int index) + + int numProps() + const char* propName(int index) + const char* propValue(int index) + double propNumber(int index) + char propType(int index) + int propIsNumber(int index) + int propIsString(int index) + + cdef cppclass lefiViaRuleLayer: + int hasDirection() + int hasEnclosure() + int hasWidth() + int hasResistance() + int hasOverhang() + int hasMetalOverhang() + int hasSpacing() + int hasRect() + + char* name() + int isHorizontal() + int isVertical() + double enclosureOverhang1() + double enclosureOverhang2() + double widthMin() + double widthMax() + double overhang() + double metalOverhang() + double resistance() + double spacingStepX() + double spacingStepY() + double xl() + double yl() + double xh() + double yh() + + cdef cppclass lefiSite: + const char* name() + int hasClass() + const char* siteClass() + double sizeX() + double sizeY() + int hasSize() + int hasXSymmetry() + int hasYSymmetry() + int has90Symmetry() + int hasRowPattern() + int numSites() + char* siteName(int index) + int siteOrient(int index) + char* siteOrientStr(int index) + + cdef cppclass lefiMacro: + int hasSize() + + const char* name() + double sizeX() + double sizeY() + + cdef cppclass lefiPin: + const char* name() + int numPorts() + lefiGeometries* port(int index) + + cdef cppclass lefiObstruction: + lefiGeometries* geometries() + + cdef cppclass lefiGeometries: + int numItems() + lefiGeomEnum itemType(int index) + const char* getLayer(int index) + double getLayerMinSpacing(int index) + double getLayerRuleWidth(int index) + double getWidth(int index) + lefiGeomPath* getPath(int index) + lefiGeomPathIter* getPathIter(int index) + lefiGeomRect* getRect(int index) + lefiGeomRectIter* getRectIter(int index) + lefiGeomPolygon* getPolygon(int index) + lefiGeomPolygonIter* getPolygonIter(int index) + lefiGeomVia* getVia(int index) + lefiGeomViaIter* getViaIter(int index) + + const char* getClass(int index) + + # Structs defining generic geometries + cdef struct lefiGeomRect: + double xl + double yl + double xh + double yh + int colorMask + + cdef struct lefiGeomRectIter: + double xl + double yl + double xh + double yh + double xStart + double yStart + double xStep + double yStep + int colorMask + + cdef struct lefiGeomPath: + int numPoints + double* x + double* y + int colorMask + + cdef struct lefiGeomPathIter: + int numPoints + double* x + double* y + double xStart + double yStart + double xStep + double yStep + int colorMask + + cdef struct lefiGeomPolygon: + int numPoints + double* x + double* y + int colorMask + + cdef struct lefiGeomPolygonIter: + int numPoints + double* x + double* y + double xStart + double yStart + double xStep + double yStep + int colorMask + + cdef struct lefiGeomVia: + char* name + double x + double y + int topMaskNum + int cutMaskNum + int bottomMaskNum + + cdef struct lefiGeomViaIter: + char* name + double x + double y + double xStart + double yStart + double xStep + double yStep + int topMaskNum + int cutMaskNum + int bottomMaskNum diff --git a/sc_leflib/_leflib.pyx b/sc_leflib/_leflib.pyx new file mode 100644 index 0000000..e4a3dd7 --- /dev/null +++ b/sc_leflib/_leflib.pyx @@ -0,0 +1,614 @@ +''' +This module implements a Python wrapper of the Si2 C++ LEF parser. It's written +using the Cython extensions for Python, which allows it to interface directly +with the C++ LEF parser. Although this file looks for the most part like +standard Python, it contains several Cython-specific constructs, and Cython +converts it into a generated C++ file before compiling it, so this code is not +interpreted as Python usually is. + +The overall structure of this wrapper is as follows: it exposes a single +top-level Python function, `parse`, which takes in a path to a LEF file, and +returns the data contained in this LEF file in the form of a dictionary (see the +docstring for parse() in __init__.py for more information about the API from a +user-facing perspective). The underlying C++ LEF parser's API is designed around +a collection of callbacks that get invoked every time the parser encounters a +particular entry in the LEF file. These callbacks receive the relevant data for +that particular entry. This wrapper defines a series of callback functions that +take this data and put it into a global object, `_state`, and after parsing is +complete the data stored in `_state` is returned to the user. + +The main special Cython constructs used in this file is the `cdef` function +declaration, which we use to define callbacks for the C++ parser. The main +difference between `cdef` and regular Python functions are function declarations +starting with cdef have C-like static type signatures. With these functions, +Cython converts transparently between C primitive types and Python equivalents, +while the interfaces to more complex objects are specified in _leflib.pxd +(another Cython-specific filetype, which can be thought as the Cython analogue +to a C/C++header file). + +Other Cython-specific things are documented inline throughout the code. + +Some pitfalls to be aware of when writing Cython code: +- Although we receive pointers to objects in all our callbacks, we can just call + methods on these directly using standard `.` notation, which Cython will + automatically convert to `->` behind the scenes. Cython does have a `deref` + operator for dereferencing pointers (equivalent to `*`), but if you're not + careful then using `deref` can result in memory errors. In paricular, we ran + into trouble with using `deref` on the RHS side of assignments in our callback + functions. When the object that stores the value of the dereferenced pointer + goes out of scope after the function returns, the object's destructor would + get called, which would then free some memory and cause double frees when the + C++ parser library attempted to free the same memory internally later on. +- Control structures like if/else don't create their own scope in Python! They + do in C/C++, but remember that this code is all semantically equivalent to + Python. +- `char*` types get converted to Python "bytes" objects instead of Python + strings. We use `.decode('ascii')` to convert the bytes to strings. + +Helpful resources: +- Cython docs: https://cython.readthedocs.io/en/latest/ +- LEF standard: http://coriolis.lip6.fr/doc/lefdef/lefdefref/lefdefref.pdf +- LEF API docs: http://coriolis.lip6.fr/doc/lefdef/lefapi/lefapi.pdf +''' + +# This let us use libc file I/O to communicate with the C++ lef library. +from libc.stdio cimport fopen, fclose + +# This imports the type/function declarations found in _leflib.pxd. +cimport _leflib + +import cython +import traceback + +from collections import OrderedDict + +# These are definitions of some custom Cython "fused types". An instance of a +# fused type can be any one of the types listed in the definition. For example, +# an instance of `RectGeometry` (as defined below) could be a pointer to a +# `lefiGeomRect or a `lefiGeomRectIter`. All of the types grouped together have +# some common interface, and using the fused types lets us make a single +# function that can deal with all of them (similar to Python's duck-typing, but +# implemented using C++ templates under the hood). In particular, these fused +# types enable us to use generic helper functions to simplify layer geometry +# extraction. +RectGeometry = cython.fused_type( + # common members: xl, yl, xh, yl + cython.pointer(lefiGeomRect), + cython.pointer(lefiGeomRectIter) +) + +PointListGeometry = cython.fused_type( + # common members: x, y, numPoints, colorMask + cython.pointer(lefiGeomPath), + cython.pointer(lefiGeomPathIter), + cython.pointer(lefiGeomPolygon), + cython.pointer(lefiGeomPolygonIter) +) + +ViaGeometry = cython.fused_type( + # common members: name, x, y, topMaskNum, cutMaskNum, bottomMaskNum + cython.pointer(lefiGeomVia), + cython.pointer(lefiGeomViaIter) +) + +IterableGeometry = cython.fused_type( + # common members: xStart, yStart, xStep, yStep + cython.pointer(lefiGeomRectIter), + cython.pointer(lefiGeomPathIter), + cython.pointer(lefiGeomPolygonIter), + cython.pointer(lefiGeomViaIter) +) + +# We hold parsed LEF data in a global that gets cleared by parse() on each call. +# The intended use of the LEF parser library is to pass around this data +# structure via the void* passed into each callback, but using a global lets us +# avoid having to deal with raw pointers to Python objects. +class ParserState: + def __init__(self): + self.clear() + + def clear(self): + self.data = {} + self.cur_macro = None + +_state = ParserState() + +# Callback functions passed to LEF parser library. The callbacks are defined in +# the order that the corresponding statements are expected to appear in a LEF +# file, as documented in the LEF standard page 12. +# +# Note that each of these functions contains the block: +# try: +# ... +# except Exception: +# traceback.print_exc() +# return 1 +# return 0 +# +# This ensures that if any sort of Python exception occurs during the callback, +# it is handled gracefully by printing out the error and returning an error code +# to the LEF parser, which will terminate parsing early and cause lefrRead() to +# return an error code downstream. +cdef int version_cb(lefrCallbackType_e cb_type, double value, lefiUserData data): + try: + _state.data['version'] = value + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int busbit_chars_cb(lefrCallbackType_e cb_type, const char* val, lefiUserData data): + try: + _state.data['busbitchars'] = val.decode('ascii') + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int divider_chars_cb(lefrCallbackType_e cb_type, const char* val, lefiUserData data): + try: + _state.data['dividerchar'] = val.decode('ascii') + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int units_cb(lefrCallbackType_e t, lefiUnits* units, lefiUserData data): + try: + if 'units' not in _state.data: + _state.data['units'] = OrderedDict() + + if units.hasDatabase(): + _state.data['units']['database'] = units.databaseNumber() + if units.hasCapacitance(): + _state.data['units']['capacitance'] = units.capacitance() + if units.hasResistance(): + _state.data['units']['resistance'] = units.resistance() + if units.hasTime(): + _state.data['units']['time'] = units.time() + if units.hasPower(): + _state.data['units']['power'] = units.power() + if units.hasCurrent(): + _state.data['units']['current'] = units.current() + if units.hasVoltage(): + _state.data['units']['voltage'] = units.voltage() + if units.hasFrequency(): + _state.data['units']['frequency'] = units.frequency() + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int manufacturing_grid_cb(lefrCallbackType_e cb_type, double value, lefiUserData data): + try: + _state.data['manufacturinggrid'] = value + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int use_min_spacing_cb(lefrCallbackType_e cb_type, lefiUseMinSpacing* minspacing, lefiUserData data): + try: + if 'useminspacing' not in _state.data: + _state.data['useminspacing'] = OrderedDict() + + # I think this should always be 'OBS', but read from the object just to + # be flexible. + name = minspacing.name().decode('ascii') + if minspacing.value() == 1: + val = 'ON' + else: + val = 'OFF' + + _state.data['useminspacing'][name] = val + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int clearance_measure_cb(lefrCallbackType_e cb_type, const char* val, lefiUserData data): + try: + _state.data['clearancemeasure'] = val.decode('ascii') + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int fixed_mask_cb(lefrCallbackType_e cb_type, int val, lefiUserData data): + try: + # I think val should always be 1. + _state.data['fixedmask'] = val == 1 + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int layer_cb(lefrCallbackType_e cb_type, lefiLayer* layer, lefiUserData data): + try: + if 'layers' not in _state.data: + _state.data['layers'] = OrderedDict() + + name = layer.name().decode('ascii') + _state.data['layers'][name] = {} + + if layer.hasType(): + _state.data['layers'][name]['type'] = layer.type().decode('ascii') + if layer.hasPitch(): + _state.data['layers'][name]['pitch'] = layer.pitch() + if layer.hasXYPitch(): + _state.data['layers'][name]['pitch'] = (layer.pitchX(), layer.pitchY()) + if layer.hasOffset(): + _state.data['layers'][name]['offset'] = layer.offset() + if layer.hasXYOffset(): + _state.data['layers'][name]['offset'] = (layer.offsetX(), layer.offsetY()) + if layer.hasWidth(): + _state.data['layers'][name]['width'] = layer.width() + if layer.hasArea(): + _state.data['layers'][name]['area'] = layer.area() + if layer.hasDirection(): + _state.data['layers'][name]['direction'] = layer.direction().decode() + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int max_via_stack_cb(lefrCallbackType_e cb_type, lefiMaxStackVia* maxstackvia, lefiUserData data): + try: + _state.data['maxviastack'] = { + 'value': maxstackvia.maxStackVia(), + } + + if maxstackvia.hasMaxStackViaRange(): + _state.data['maxviastack']['range'] = { + 'bottom': maxstackvia.maxStackViaBottomLayer().decode('ascii'), + 'top': maxstackvia.maxStackViaTopLayer().decode('ascii') + } + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int viarule_cb(lefrCallbackType_e cb_type, lefiViaRule* viarule, void* data): + try: + if 'viarules' not in _state.data: + _state.data['viarules'] = OrderedDict() + + viarule_data = {} + if viarule.hasDefault(): + viarule_data['default'] = True + if viarule.hasGenerate(): + viarule_data['generate'] = True + + layers = [] + for i in range(viarule.numLayers()): + layer = viarule.layer(i) + layer_data = {'name': layer.name().decode('ascii')} + # nongenerate only + if layer.hasDirection(): + layer_data['direction'] = 'HORIZONTAL' if layer.isHorizontal() else 'VERTICAL' + # generate only + if layer.hasEnclosure(): + layer_data['enclosure'] = { + 'overhang1': layer.enclosureOverhang1(), + 'overhang2': layer.enclosureOverhang2() + } + if layer.hasRect(): + layer_data['rect'] = (layer.xl(), layer.yl(), layer.xh(), layer.xh()) + if layer.hasSpacing(): + layer_data['spacing'] = { + 'x': layer.spacingStepX(), + 'y': layer.spacingStepY() + } + if layer.hasResistance(): + layer_data['resistance'] = layer.resistance() + + # nongenerate and generate + if layer.hasWidth(): + layer_data['width'] = { + 'min': layer.widthMin(), + 'max': layer.widthMax() + } + + layers.append(layer_data) + + vias = [] + for i in range(viarule.numVias()): + vias.append(viarule.viaName(i).decode('ascii')) + + if len(layers) > 0: + viarule_data['layers'] = layers + if len(vias) > 0: + viarule_data['vias'] = vias + + name = viarule.name().decode('ascii') + _state.data['viarules'][name] = viarule_data + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int site_cb(lefrCallbackType_e cb_type, lefiSite* site, lefiUserData data): + try: + if 'sites' not in _state.data: + _state.data['sites'] = OrderedDict() + + site_data = {} + if site.hasClass(): + site_data['class'] = site.siteClass().decode('ascii') + + symmetries = [] + if site.hasXSymmetry(): + symmetries.append('X') + if site.hasYSymmetry(): + symmetries.append('Y') + if site.has90Symmetry(): + symmetries.append('R90') + if len(symmetries) > 0: + site_data['symmetry'] = symmetries + + rowpattern = [] + for i in range(site.numSites()): + rowpattern.append({ + 'name': site.siteName(i).decode('ascii'), + 'orient': site.siteOrientStr(i).decode('ascii') + }) + if len(rowpattern) > 0: + site_data['rowpattern'] = rowpattern + + if site.hasSize(): + site_data['size'] = { + 'width': site.sizeX(), + 'height': site.sizeY() + } + + name = site.name().decode('ascii') + _state.data['sites'][name] = site_data + except Exception: + traceback.print_exc() + return 1 + return 0 + +# Macro information is passed in through several different callbacks. First, +# `macro_begin_cb` is called to start a new "macro session", so we just store +# the name of the current macro in `_state.cur_macro` here. `pin_cb` and +# `obs_cb` are then called for each PIN and OBS statement in the current macro, +# and finally `macro_cb` is called with the remaining information for the +# current macro. +cdef int macro_begin_cb(lefrCallbackType_e cb_type, const char* name, lefiUserData data): + try: + if 'macros' not in _state.data: + _state.data['macros'] = OrderedDict() + + _state.cur_macro = name.decode('ascii') + _state.data['macros'][_state.cur_macro] = {} + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int pin_cb(lefrCallbackType_e cb_type, lefiPin* pin, lefiUserData data): + try: + if 'pins' not in _state.data['macros'][_state.cur_macro]: + _state.data['macros'][_state.cur_macro]['pins'] = {} + + name = pin.name().decode('ascii') + _state.data['macros'][_state.cur_macro]['pins'][name] = {} + + ports = [] + for i in range(pin.numPorts()): + port = pin.port(i) + port_data = {} + + # The CLASS of a port is stored in its list of items, so search for + # that here. + for j in range(port.numItems()): + if port.itemType(j) == lefiGeomClassE: + port_data['class'] = port.getClass(j).decode('ascii') + + # Otherwise, the other port "items" all refer to layerGeometries, + # which are shared by several other types of things, so we extract + # them using a separate helper function. + geometries = extract_layer_geometries(port) + if len(geometries) > 0: + port_data['layer_geometries'] = geometries + + ports.append(port_data) + + if len(ports) > 0: + _state.data['macros'][_state.cur_macro]['pins'][name]['ports'] = ports + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int obs_cb(lefrCallbackType_e cb_type, lefiObstruction* obs, lefiUserData data): + try: + if 'obs' not in _state.data['macros'][_state.cur_macro]: + _state.data['macros'][_state.cur_macro]['obs'] = [] + + geometries = extract_layer_geometries(obs.geometries()) + + # Append geometries even if empty so that the dictionary reflects how + # many OBS appear in the LEF (even if they're empty). + _state.data['macros'][_state.cur_macro]['obs'].append(geometries) + except Exception: + traceback.print_exc() + return 1 + return 0 + +cdef int macro_cb(lefrCallbackType_e cb_type, lefiMacro* macro, void* data): + try: + if macro.hasSize(): + _state.data['macros'][_state.cur_macro]['size'] = { + 'width': macro.sizeX(), + 'height': macro.sizeY() + } + except Exception: + traceback.print_exc() + return 1 + return 0 + +# The following functions are used to extract generic objects that are used in +# several contexts by the LEF parser into Python lists or dictionaries. +cdef extract_points(PointListGeometry geo): + points = [] + for i in range(geo.numPoints): + pt = (geo.x[i], geo.y[i]) + points.append(pt) + return points + +cdef extract_rect(RectGeometry rect): + return (rect.xl, rect.yl, rect.xh, rect.yh) + +cdef extract_via(ViaGeometry via): + top = hex(via.topMaskNum)[2:] + cut = hex(via.cutMaskNum)[2:] + bot = hex(via.bottomMaskNum)[2:] + + return { + 'pt': (via.x, via.y), + 'name': via.name.decode('ascii'), + 'mask': top+cut+bot + } + +cdef extract_iterate(IterableGeometry iterable): + return { + 'num_x': iterable.xStart, + 'num_y': iterable.yStart, + 'step_x': iterable.xStep, + 'step_y': iterable.yStep + } + +cdef extract_layer_geometries(lefiGeometries* geos): + '''Extracts layer geometries used for defining pin ports and obs geometries. + + See "Layer Geometries" in the LEF standard page 130. + ''' + geometries = [] + cur_geometry = {} + + for i in range(geos.numItems()): + geo_type = geos.itemType(i) + + if geo_type == lefiGeomLayerE: + # Geometries start with LAYER statements. If we already have a + # geometry actively being worked on, we append it to the list + # and start a new one. + if cur_geometry != {}: + geometries.append(cur_geometry) + cur_geometry = {} + + cur_geometry['layer'] = geos.getLayer(i).decode('ascii') + cur_geometry['shapes'] = [] + elif geo_type == lefiGeomLayerExceptPgNetE: + cur_geometry['exceptpgnet'] = True + elif geo_type == lefiGeomLayerMinSpacingE: + cur_geometry['spacing'] = geos.getLayerMinSpacing(i) + elif geo_type == lefiGeomLayerRuleWidthE: + cur_geometry['designrulewidth'] = geos.getLayerRuleWidth(i) + elif geo_type == lefiGeomWidthE: + cur_geometry['width'] = geos.getWidth(i) + elif geo_type == lefiGeomPathE: + path = geos.getPath(i) + cur_geometry['shapes'].append({ + 'path': extract_points(path), + 'mask': path.colorMask, + }) + elif geo_type == lefiGeomPathIterE: + pathiter = geos.getPathIter(i) + cur_geometry['shapes'].append({ + 'path': extract_points(pathiter), + 'mask': pathiter.colorMask, + 'iterate': extract_iterate(pathiter) + }) + elif geo_type == lefiGeomRectE: + rect = geos.getRect(i) + cur_geometry['shapes'].append({ + 'rect': extract_rect(rect), + 'mask': rect.colorMask, + }) + elif geo_type == lefiGeomRectIterE: + rectiter = geos.getRectIter(i) + cur_geometry['shapes'].append({ + 'rect': extract_rect(rectiter), + 'mask': rectiter.colorMask, + 'iterate': extract_iterate(rectiter) + }) + elif geo_type == lefiGeomPolygonE: + poly = geos.getPolygon(i) + cur_geometry['shapes'].append({ + 'polygon': extract_points(poly), + 'mask': poly.colorMask + }) + elif geo_type == lefiGeomPolygonIterE: + polyiter = geos.getPolygonIter(i) + cur_geometry['shapes'].append({ + 'polygon': extract_points(polyiter), + 'mask': polyiter.colorMask, + 'iterate': extract_iterate(polyiter) + }) + elif geo_type == lefiGeomViaE: + via = geos.getVia(i) + cur_geometry['via'] = extract_via(via) + elif geo_type == lefiGeomViaIterE: + viaiter = geos.getViaIter(i) + cur_geometry['via'] = extract_via(viaiter) + cur_geometry['via']['iterate'] = extract_iterate(viaiter) + + if cur_geometry != {}: + geometries.append(cur_geometry) + + return geometries + +# Additional callback for logging warnings/errors. +cdef void log(const char* msg): + print(msg.decode('ascii')) + +# This is the module's main entry point and the single Python function exposed +# by this module. +def parse(path): + ''' See leflib/__init__.py for full docstring. We put it there to ensure + it's picked up by Sphinx.''' + + _state.clear() + + if lefrInit() != 0: + return None + + lefrSetVersionCbk(version_cb) + lefrSetBusBitCharsCbk(busbit_chars_cb) + lefrSetDividerCharCbk(divider_chars_cb) + lefrSetUnitsCbk(units_cb) + lefrSetManufacturingCbk(manufacturing_grid_cb) + lefrSetUseMinSpacingCbk(use_min_spacing_cb) + lefrSetClearanceMeasureCbk(clearance_measure_cb) + lefrSetFixedMaskCbk(fixed_mask_cb) + lefrSetLayerCbk(layer_cb) + lefrSetMaxStackViaCbk(max_via_stack_cb) + lefrSetViaRuleCbk(viarule_cb) + lefrSetSiteCbk(site_cb) + lefrSetMacroBeginCbk(macro_begin_cb) + lefrSetPinCbk(pin_cb) + lefrSetObstructionCbk(obs_cb) + lefrSetMacroCbk(macro_cb) + + lefrSetLogFunction(log) + lefrSetWarningLogFunction(log) + + # Use this to pass path to C++ functions + path_bytes = path.encode('ascii') + + f_ptr = fopen(path_bytes, 'r') + if f_ptr == NULL: + print("Couldn't open file " + path) + return None + + r = lefrRead(f_ptr, path_bytes, NULL) + + fclose(f_ptr) + + if r != 0: + print("Error parsing LEF!") + return None + + return _state.data diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..1466f90 --- /dev/null +++ b/setup.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +import os +import shutil +from skbuild import setup +from setuptools_scm import get_version + +with open("README.md", "r", encoding="utf-8") as readme: + long_desc = readme.read() + + +def parse_reqs(): + '''Parse out each requirement category from requirements.txt''' + install_reqs = [] + extras_reqs = {} + current_section = None # default to install + + with open('requirements.txt', 'r') as reqs_file: + for line in reqs_file.readlines(): + line = line.rstrip('\n') + if line.startswith('#:'): + # strip off '#:' prefix to read extras name + current_section = line[2:] + if current_section not in extras_reqs: + extras_reqs[current_section] = [] + elif not line or line.startswith('#'): + # skip blanks and comments + continue + elif current_section is None: + install_reqs.append(line) + else: + extras_reqs[current_section].append(line) + + return install_reqs, extras_reqs + + +# Let us pass in generic arguments to CMake via an environment variable, since +# our automated build servers need to pass in a certain argument when building +# wheels on Windows. +cmake_args = [] +if 'SC_CMAKEARGS' in os.environ: + cmake_args.append(os.environ['SC_CMAKEARGS']) + +# Remove the _skbuild/ directory before running install procedure. This helps +# fix very opaque bugs we've run into where the install fails due to some bad +# state being cached in this directory. This means we won't get caching of build +# results, but since the leflib is small and compiles quickly, and a user likely +# won't have to perform many installs anyways, this seems like a worthwhile +# tradeoff. +if os.path.isdir('_skbuild'): + print("Note: removing existing _skbuild/ directory.") + shutil.rmtree('_skbuild') + +skbuild_args = { + 'cmake_install_dir': 'sc_leflib', + 'cmake_args': cmake_args +} + + +install_reqs, extras_req = parse_reqs() + +setup( + name="sc-leflib", + description="LEF parser for SiliconCompiler", + long_description=long_desc, + long_description_content_type="text/markdown", + license='Apache License 2.0', + author="ZeroASIC", + author_email="gadfort@zeroasic.com", + url="https://siliconcompiler.com", + project_urls={ + "Documentation": "https://docs.siliconcompiler.com", + "Source Code": "https://github.com/siliconcompiler/sc-leflib", + "Bug Tracker": "https://github.com/siliconcompiler/sc-leflib/issues", + "Forum": "https://github.com/siliconcompiler/sc-leflib/discussions" + }, + version=get_version(), + packages=["sc_leflib"], + + python_requires=">=3.6", + install_requires=install_reqs, + extras_require=extras_req, + **skbuild_args +) diff --git a/siliconcompiler b/siliconcompiler new file mode 160000 index 0000000..25990b2 --- /dev/null +++ b/siliconcompiler @@ -0,0 +1 @@ +Subproject commit 25990b21117944dbd9d1fcca995638fb4e937879 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..a0f170c --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,17 @@ +import pytest +import os + + +@pytest.fixture +def scroot(): + '''Returns an absolute path to the SC root directory.''' + mydir = os.path.dirname(__file__) + return os.path.abspath(os.path.join(mydir, '..', 'siliconcompiler')) + + +@pytest.fixture +def datadir(request): + '''Returns an absolute path to the current test directory's local data + directory.''' + mydir = os.path.dirname(request.fspath) + return os.path.abspath(os.path.join(mydir, 'data')) diff --git a/tests/data/complete.5.8.lef b/tests/data/complete.5.8.lef new file mode 100644 index 0000000..a5a2108 --- /dev/null +++ b/tests/data/complete.5.8.lef @@ -0,0 +1,1962 @@ +VERSION 5.8 ; +NAMESCASESENSITIVE ON ; +FIXEDMASK ; +NOWIREEXTENSIONATPIN ON ; +BUSBITCHARS "<>" ; +DIVIDERCHAR ":" ; +USEMINSPACING OBS OFF ; +USEMINSPACING PIN ON ; +CLEARANCEMEASURE EUCLIDEAN ; +CLEARANCEMEASURE MAXXY ; + +&defines &VDD/GND_site = "VDDGND" ; +&defines &VDD/GND_site = "VDDGND1" ; + +UNITS + TIME NANOSECONDS 100 ; + CAPACITANCE PICOFARADS 10 ; + RESISTANCE OHMS 10000 ; + POWER MILLIWATTS 10000 ; + CURRENT MILLIAMPS 10000 ; + VOLTAGE VOLTS 1000 ; + DATABASE MICRONS 20000 ; + FREQUENCY MEGAHERTZ 10 ; +END UNITS + +MANUFACTURINGGRID 3.5 ; + +PROPERTYDEFINITIONS + LIBRARY NAME STRING "Cadence96" ; + LIBRARY intNum INTEGER 20 ; + LIBRARY realNum REAL 21.22 ; + LIBRARY LEF57_MAXFLOATINGAREAGATE STRING "MAXFLOATINGAREA GATEISGROUND;" ; + LAYER lsp STRING ; + LAYER lip INTEGER ; + LAYER lrp REAL ; + LAYER LEF57_SPACING STRING ; + LAYER LEF57_SPACINGADJACENTCUTS STRING ; + LAYER LEF57_MAXFLOATINGAREA STRING ; + LAYER LEF57_ARRAYSPACING STRING ; + LAYER LEF57_SPACINGSAMENET STRING ; + LAYER LEF57_MINSTEP STRING ; + LAYER LEF57_ANTENNAGATEPLUSDIFF STRING ; + LAYER LEF57_ANTENNACUMROUTINGPLUSCUT STRING ; + LAYER LEF57_ANTENNAAREAMINUSDIFF STRING ; + LAYER LEF57_ANTENNAAREADIFFREDUCEPWL STRING ; + LAYER LEF57_ENCLOSURE STRING ; + VIA stringProperty STRING ; + VIA realProperty REAL ; + VIA COUNT INTEGER RANGE 1 100 ; + VIARULE vrsp STRING ; + VIARULE vrip INTEGER ; + VIARULE vrrp REAL ; + NONDEFAULTRULE ndrsp STRING ; + NONDEFAULTRULE ndrip INTEGER ; + NONDEFAULTRULE ndrrp REAL ; + MACRO stringProp STRING ; + MACRO integerProp INTEGER ; + MACRO WEIGHT REAL RANGE 1.0 100.0 ; + PIN TYPE STRING ; + PIN intProp INTEGER ; + PIN realProp REAL ; +END PROPERTYDEFINITIONS + +LAYER POLYS + TYPE MASTERSLICE ; + PROPERTY lsp "top" lip 1 lrp 2.3 ; +END POLYS + +LAYER POLYS01 + TYPE MASTERSLICE ; +END POLYS01 + +LAYER CUT01 + TYPE CUT ; + SPACING 0.35 ADJACENTCUTS 3 WITHIN 0.25 ; + DIAGPITCH 6.5 ; + OFFSET 0.5 .6 ; + PITCH 1.2 1.3 ; + PROPERTY lip 5 ; + PROPERTY LEF57_SPACING "SPACING 1.5 PARALLELOVERLAP ;" ; + PROPERTY LEF57_ARRAYSPACING "ARRAYSPACING WIDTH 2.0 CUTSPACING 0.2 ARRAYCUTS 3 SPACING 1.0 ;" ; + PROPERTY LEF57_ENCLOSURE "ENCLOSURE ABOVE .01 .05 ;" ; + PROPERTY LEF57_ENCLOSURE "ENCLOSURE ABOVE .02 .05 WIDTH 3.1 EXCEPTEXTRACUT 1.5 NOSHAREDEDGE ;" ; + PROPERTY LEF57_ENCLOSURE "ENCLOSURE BELOW .03 .05 WIDTH 3.1 EXCEPTEXTRACUT 1.5 ;" ; + PROPERTY LEF57_ENCLOSURE "ENCLOSURE .05 .05 ;" ; + PROPERTY LEF57_ENCLOSURE "ENCLOSURE BELOW .08 .05 WIDTH 3.1 EXCEPTEXTRACUT 1.5 NOSHAREDEDGE ;" ; +END CUT01 + +LAYER RX + TYPE ROUTING ; + MASK 2 ; + PITCH 1.8 ; + OFFSET 0.9 ; + WIDTH 1 ; + AREA 34.1 ; + MINIMUMCUT 2 WIDTH 2.5 ; + SPACING 0.6 ; + SPACING 0.18 LENGTHTHRESHOLD 0.9 ; + SPACING 0.4 RANGE 0.1 0.12 ; + SPACING 0.32 RANGE 1.01 2000.0 USELENGTHTHRESHOLD ; + SPACING 0.1 RANGE 0.1 0.1 INFLUENCE 2.01 RANGE 2.1 10000.0 ; + SPACING 0.44 RANGE 1.0 1.0 INFLUENCE 1.01 ; + SPACING 0.33 RANGE 1.01 20.0 INFLUENCE 1.01 ; + SPACING 0.7 RANGE 0.3 0.15 USELENGTHTHRESHOLD ; + SPACING 0.5 ; + SPACING 0.6 RANGE 4.5 6.12 RANGE 3.0 3.1 ; + SPACING 4.3 RANGE 0.1 0.1 INFLUENCE 3.81 RANGE 0.1 0.2 ; + SPACING 0.53 LENGTHTHRESHOLD 0.45 RANGE 0 0.1 ; + PROPERTY LEF57_SPACING "SPACING 2.2 ENDOFLINE 2.3 WITHIN 1.6 ;" ; + PROPERTY LEF57_ARRAYSPACING "ARRAYSPACING LONGARRAY CUTSPACING 0.2 ARRAYCUTS 3 SPACING 1.0 ARRAYCUTS 4 SPACING 1.5 ARRAYCUTS 5 SPACING 2.0 ;" ; + DIRECTION HORIZONTAL ; + WIREEXTENSION 0.75 ; + RESISTANCE RPERSQ 0.103 ; + CAPACITANCE CPERSQDIST 0.000156 ; + HEIGHT 9 ; + THICKNESS 1 ; + SHRINKAGE 0.1 ; + SLOTWIREWIDTH 5 ; + SLOTWIRELENGTH 4 ; + SLOTWIDTH 6 ; + SLOTLENGTH 5 ; + MAXADJACENTSLOTSPACING 45 ; + MAXCOAXIALSLOTSPACING 55 ; + SPLITWIREWIDTH 5 ; + MINIMUMDENSITY 4 ; + MAXIMUMDENSITY 10 ; +# DENSITYCHECKWINDOW 4 5 ; + DENSITYCHECKSTEP 2 ; + FILLACTIVESPACING 4 ; + CAPMULTIPLIER 1 ; + EDGECAPACITANCE 0.00005 ; + ANTENNAMODEL OXIDE1 ; + ANTENNAAREAFACTOR 1 ; + ANTENNAMODEL OXIDE2 ; + ANTENNAAREARATIO 4.6 ; + ANTENNAAREARATIO 7.6 ; + ANTENNADIFFAREARATIO 4.7 ; + ANTENNADIFFAREARATIO PWL ( ( 5.4 5.4 ) ( 6.5 6.5 ) ( 7.5 7.5 ) ) ; + ANTENNACUMAREARATIO 6.7 ; # 5.7 + ANTENNAAREAFACTOR 10 ; # 5.7 + ANTENNACUMROUTINGPLUSCUT ; # 5.7 + ANTENNAAREAMINUSDIFF 100.0 ; # 5.7 + ANTENNAGATEPLUSDIFF 2.0 ; # 5.7 + ANTENNACUMDIFFAREARATIO 1000 ; # 5.7 + ANTENNAAREADIFFREDUCEPWL ( ( 0.0 1.0 ) ( 0.09999 1.0 ) ( 0.1 0.2 ) ( 1.0 0.1 ) ( 1000.0 0.1 ) ) ; + ANTENNACUMDIFFAREARATIO 1000 ; # 5.7 + PROPERTY LEF57_ANTENNACUMROUTINGPLUSCUT "ANTENNACUMROUTINGPLUSCUT ;" ; + PROPERTY LEF57_ANTENNAAREAMINUSDIFF "ANTENNAAREAMINUSDIFF 100.0 ;" ; + PROPERTY LEF57_ANTENNAGATEPLUSDIFF "ANTENNAGATEPLUSDIFF 2.0 ;" ; + PROPERTY LEF57_ANTENNAAREADIFFREDUCEPWL "ANTENNAAREADIFFREDUCEPWL ( ( 0.0 1.0 ) ( 0.0999 1.0 ) ( 0.1 0.2 ) ( 1.0 0.1 ) ( 1000.0 0.1 ) ) ;" ; + ANTENNACUMDIFFAREARATIO 4.5 ; + ANTENNACUMDIFFAREARATIO PWL ( ( 5.4 5.4 ) ( 6.5 6.5 ) ( 7.5 7.5 ) ) ; + ANTENNAAREAFACTOR 6.5 ; + ANTENNAAREAFACTOR 6.5 DIFFUSEONLY ; + ANTENNASIDEAREARATIO 6.5 ; + ANTENNADIFFSIDEAREARATIO 6.5 ; + ANTENNADIFFSIDEAREARATIO PWL ( ( 5.4 5.4 ) ( 6.5 6.5 ) ( 7.5 7.5 ) ) ; + ANTENNACUMSIDEAREARATIO 4.5 ; + ANTENNACUMSIDEAREARATIO 7.5 ; + ANTENNACUMDIFFSIDEAREARATIO 4.6 ; + ANTENNACUMDIFFSIDEAREARATIO PWL ( ( 5.4 5.4 ) ( 6.5 6.5 ) ( 7.5 7.5 ) ) ; + ANTENNASIDEAREAFACTOR 6.5 ; + ANTENNASIDEAREAFACTOR 7.5 DIFFUSEONLY ; + ANTENNAMODEL OXIDE3 ; + ANTENNAMODEL OXIDE4 ; + PROPERTY lsp "rxlay" lip 3 lrp 1.2 ; + CURRENTDEN 1E3 ; +# CURRENTDEN ( 1E3 4E5 ) ; + ACCURRENTDENSITY PEAK + FREQUENCY 1E6 100E6 ; + TABLEENTRIES 0.5E-6 0.4E-6 ; + ACCURRENTDENSITY AVERAGE 5.5 ; + ACCURRENTDENSITY RMS + FREQUENCY 100E6 400E6 800E6 ; + WIDTH 0.4 0.8 10.0 50.0 100.0 ; + TABLEENTRIES + 2.0E-6 1.9E-6 1.8E-6 1.7E-6 1.5E-6 + 1.4E-6 1.3E-6 1.2E-6 1.1E-6 1.0E-6 + 0.9E-6 0.8E-6 0.7E-6 0.6E-6 0.4E-6 ; + DCCURRENTDENSITY AVERAGE + WIDTH 20.0 50.0 ; + TABLEENTRIES 0.6E-6 0.5E-6 ; +END RX + +LAYER CUT12 +TYPE CUT ; + DIAGPITCH 1.5 1.7 ; + DIAGWIDTH 1.6 ; + DIAGSPACING 0.5 ; + SPACING 0.7 LAYER RX ; + SPACING 0.22 ADJACENTCUTS 4 WITHIN 0.25 ; + SPACING 1.5 PARALLELOVERLAP ; # 5.7 + SPACING 1.2 ADJACENTCUTS 2 WITHIN 1.5 EXCEPTSAMEPGNET ; # 5.7 + # 5.4 + ANTENNAMODEL OXIDE1 ; + ANTENNAMODEL OXIDE2 ; + ANTENNAMODEL OXIDE3 ; + ANTENNAMODEL OXIDE4 ; + ANTENNAAREAFACTOR 5.4 ; + ANTENNACUMROUTINGPLUSCUT ; # 5.7 + ANTENNAAREAMINUSDIFF 100.0 ; # 5.7 + ANTENNAGATEPLUSDIFF 2.0 ; # 5.7 + ANTENNADIFFAREARATIO 1000 ; # 5.7 + ANTENNACUMDIFFAREARATIO 5000 ; # 5.7 + ANTENNADIFFAREARATIO 6.5 ; + ANTENNAAREADIFFREDUCEPWL ( ( 0.0 1.0 ) ( 0.09999 1.0 ) ( 0.1 0.2 ) ( 1.0 0.1 ) ( 1000.0 0.1 ) ) ; # 5.7 + ANTENNADIFFAREARATIO PWL ( ( 5.4 5.4 ) ( 6.5 6.5 ) ( 7.5 7.5 ) ) ; + ANTENNACUMDIFFAREARATIO PWL ( ( 5.4 5.4 ) ( 6.5 6.5 ) ( 7.5 7.5 ) ) ; + ANTENNACUMDIFFAREARATIO 5.6 ; + ANTENNAAREARATIO 5.6 ; + ANTENNACUMAREARATIO 6.7 ; + ACCURRENTDENSITY PEAK + FREQUENCY 1E6 100E6 ; + TABLEENTRIES 0.5E-6 0.4E-6 ; + ACCURRENTDENSITY AVERAGE 5.5 ; + ACCURRENTDENSITY RMS + FREQUENCY 100E6 400E6 800E6 ; + CUTAREA 0.4 0.8 10.0 50.0 100.0 ; + TABLEENTRIES + 2.0E-6 1.9E-6 1.8E-6 1.7E-6 1.5E-6 + 1.4E-6 1.3E-6 1.2E-6 1.1E-6 1.0E-6 + 0.9E-6 0.8E-6 0.7E-6 0.6E-6 0.4E-6 ; + DCCURRENTDENSITY AVERAGE + CUTAREA 2.0 5.0 ; + TABLEENTRIES 0.5E-6 0.4E-6 ; + DCCURRENTDENSITY AVERAGE 4.9 ; +END CUT12 + +LAYER PC + TYPE ROUTING ; + WIDTH 1 ; + WIREEXTENSION 0.4 ; #should be ignored + PITCH 3.8 3.5 ; + DIAGPITCH 1.4 ; + SPACING 0.6 ; + SPACING 1.2 ENDOFLINE 1.3 WITHIN 0.6 ; # 5.7 + SPACING 1.3 ENDOFLINE 1.4 WITHIN 0.7 PARALLELEDGE 1.1 WITHIN 0.5 TWOEDGES ; + SPACING 1.4 ENDOFLINE 1.5 WITHIN 0.8 PARALLELEDGE 1.2 WITHIN 0.6 ; # 5.7 + DIRECTION VERTICAL ; + RESISTANCE RPERSQ PWL ( ( 1 0.103 ) ( 10 4.7 ) ) ; + CAPACITANCE CPERSQDIST PWL ( ( 1 0.000156 ) ( 10 0.001 ) ) ; + ANTENNAAREARATIO 5.4 ; + ANTENNADIFFAREARATIO 6.5 ; + ANTENNACUMAREARATIO 7.5 ; + ANTENNACUMDIFFAREARATIO PWL ( ( 5.0 5.1 ) ( 6.0 6.1 ) ) ; + ANTENNAAREAFACTOR 4.5 ; + ANTENNASIDEAREARATIO 6.5 ; + ANTENNADIFFSIDEAREARATIO PWL ( ( 7.0 7.1 ) ( 7.2 7.3 ) ) ; + ANTENNACUMSIDEAREARATIO 7.4 ; + ANTENNACUMDIFFSIDEAREARATIO PWL ( ( 8.0 8.1 ) ( 8.2 8.3 ) ( 8.4 8.5 ) + ( 8.6 8.7 ) ) ; + ANTENNASIDEAREAFACTOR 9.0 DIFFUSEONLY ; + + ACCURRENTDENSITY PEAK + FREQUENCY 1E6 100E6 ; + WIDTH 5.6 8.5 8.1 4.5 ; + TABLEENTRIES 0.5E-6 0.4E-6 ; + DCCURRENTDENSITY AVERAGE + WIDTH 20.0 50.0 100.0 ; + TABLEENTRIES 1.0E-6 0.7E-6 0.5E-6 ; +END PC + +LAYER CA + TYPE CUT ; + DCCURRENTDENSITY AVERAGE + CUTAREA 2.0 5.0 10.0 ; + TABLEENTRIES 0.6E-6 0.5E-6 0.4E-6 ; +END CA + +LAYER M1 + TYPE ROUTING ; + WIDTH 1 ; + WIREEXTENSION 7 ; + PITCH 1.8 ; + DIRECTION HORIZONTAL ; + RESISTANCE RPERSQ 0.103 ; + CAPACITANCE CPERSQDIST 0.000156 ; + ANTENNACUMAREARATIO 300 ; + ANTENNACUMDIFFAREARATIO 600 ; + ANTENNAGATEPLUSDIFF 2.0 ; # 5.7 + ANTENNADIFFAREARATIO 1000 ; # 5.7 + ANTENNACUMDIFFAREARATIO 5000 ; # 5.7 + SPACINGTABLE + PARALLELRUNLENGTH 0.00 0.50 3.00 5.00 + WIDTH 0.00 0.15 0.15 0.15 0.15 + WIDTH 0.25 0.15 0.20 0.20 0.20 + WIDTH 1.50 0.15 0.50 0.50 0.50 + WIDTH 3.00 0.15 0.50 1.00 1.00 + WIDTH 5.00 0.15 0.50 1.00 2.00 ; + SPACINGTABLE + INFLUENCE + WIDTH 1.5 WITHIN 0.5 SPACING 0.5 + WIDTH 3.0 WITHIN 1.0 SPACING 1.0 + WIDTH 5.0 WITHIN 2.0 SPACING 2.0 ; + ACCURRENTDENSITY AVERAGE 5.5 ; + DCCURRENTDENSITY AVERAGE 4.9 ; +END M1 + +LAYER V1 + TYPE CUT ; + SPACING 0.6 LAYER CA ; +END V1 + +LAYER M2 + TYPE ROUTING ; + WIDTH 0.9 ; + WIREEXTENSION 8 ; + PITCH 1.8 ; + SPACING 0.9 ; + SPACING 0.28 ; + SPACING 0.24 LENGTHTHRESHOLD 1.0 ; + SPACING 0.32 RANGE 1.01 9.99 USELENGTHTHRESHOLD ; + SPACING 0.5 RANGE 10.0 1000.0 ; + SPACING 0.5 RANGE 10.0 1000.0 INFLUENCE 1.00 ; + SPACING 0.5 RANGE 10.0 1000.0 INFLUENCE 1.0 RANGE .28 1.0 ; + SPACING 0.5 RANGE 3.01 4.0 RANGE 4.01 5.0 ; + SPACING 0.4 RANGE 3.01 4.0 RANGE 5.01 1000.0 ; + SPACING 1.0 SAMENET PGONLY ; # 5.7 + SPACING 1.1 SAMENET ; # 5.7 + PROPERTY LEF57_SPACING "SPACING 1.2 ENDOFLINE 1.3 WITHIN 0.6 PARALLELEDGE 2.1 WITHIN 1.5 TWOEDGES ;" ; + PROPERTY LEF57_SPACING "SPACING 1.5 ENDOFLINE 2.3 WITHIN 1.6 PARALLELEDGE 1.1 WITHIN 0.5 ;" ; + DIRECTION DIAG45 ; + RESISTANCE RPERSQ 0.0608 ; + CAPACITANCE CPERSQDIST 0.000184 ; + PROPERTY LEF57_MAXFLOATINGAREA "MAXFLOATINGAREA 1000 ;" ; + ANTENNAMODEL OXIDE1 ; + ANTENNACUMAREARATIO 5000 ; + ANTENNACUMDIFFAREARATIO 8000 ; + ANTENNAMODEL OXIDE2 ; + ANTENNACUMAREARATIO 500 ; + ANTENNACUMDIFFAREARATIO 800 ; + ANTENNAMODEL OXIDE3 ; + ANTENNACUMAREARATIO 300 ; + ANTENNACUMDIFFAREARATIO 600 ; +END M2 + +LAYER V2 + TYPE CUT ; +END V2 + +LAYER M3 + TYPE ROUTING ; + WIDTH 0.9 ; + WIREEXTENSION 8 ; + PITCH 1.8 ; + SPACING 0.9 ; + DIRECTION HORIZONTAL ; + RESISTANCE RPERSQ 0.0608 ; + CAPACITANCE CPERSQDIST 0.000184 ; + ANTENNAMODEL OXIDE3 ; + ANTENNACUMAREARATIO 5000 ; + ANTENNACUMDIFFAREARATIO 8000 ; + ANTENNAMODEL OXIDE4 ; + ANTENNACUMAREARATIO 500 ; + ANTENNACUMDIFFAREARATIO 800 ; + ANTENNAMODEL OXIDE1 ; + ANTENNACUMAREARATIO 300 ; + ANTENNACUMDIFFAREARATIO 600 ; + PROPERTY LEF57_MINSTEP "MINSTEP 1.0 MAXEDGES 2 ;" ; +END M3 + +LAYER M4 + TYPE ROUTING ; + PITCH 5.4 ; + WIDTH 5.4 ; + DIRECTION VERTICAL ; + DIRECTION HORIZONTAL ; + # 2 via cuts required for m4 > 0.50 um when connecting from m3 + MINIMUMCUT 2 WIDTH 0.50 ; + # 2 via cuts required for m4 > 0.70 um when connecting from m5 + MINIMUMCUT 2 WIDTH 0.70 FROMBELOW ; + MINIMUMCUT 3 WIDTH 0.80 WITHIN 0.3 ; # 5.7 + MINIMUMCUT 2 WIDTH 1.00 FROMBELOW LENGTH 20.0 WITHIN 5.0 ; + # 4 via cuts are required for m4 > 1.0 um when connecting from m3 or m5 + MINIMUMCUT 4 WIDTH 1.0 FROMABOVE ; + # 2 via cuts are required if m4 > 1.1 um wide and m4 > 20.0 um long, + # and the via cut is < 5.0 um away from the wide wire + MINIMUMCUT 2 WIDTH 1.1 LENGTH 20.0 WITHIN 5.0 ; + MINIMUMCUT 2 WIDTH 1.1 FROMABOVE LENGTH 20.0 WITHIN 5.0 ; + MINENCLOSEDAREA 0.30 ; # donut hole must be >= 0.30 um^2 + MINENCLOSEDAREA 0.40 WIDTH 0.15 ; # hole area >= 0.40 um^2 when w<=0.15 + MINENCLOSEDAREA 0.80 WIDTH 0.50 ; # hole area >= 0.80 um^2 when w<=0.55 + MAXWIDTH 10.0 ; + MINWIDTH 0.15 ; + PROTRUSIONWIDTH 0.30 LENGTH 0.60 WIDTH 1.20 ; + MINSTEP .20 ; +END M4 + +LAYER M5 + TYPE ROUTING ; + PITCH 5.4 ; + WIDTH 4.0 ; + DIRECTION DIAG135 ; + MINSTEP 0.05 ; + MINSTEP 0.04 ; + MINSTEP 0.05 LENGTHSUM 0.08 ; + MINSTEP 0.05 LENGTHSUM 0.16 ; + MINSTEP 0.05 INSIDECORNER ; + MINSTEP 0.05 INSIDECORNER LENGTHSUM 0.15 ; + MINSTEP 1.0 MAXEDGES 2 ; # 5.7 + MINIMUMCUT 2 WIDTH 0.70 ; + MINIMUMCUT 4 WIDTH 1.0 FROMABOVE ; + MINIMUMCUT 2 WIDTH 1.1 LENGTH 20.0 WITHIN 5.0 ; + MINIMUMCUT 5 WIDTH 0.5 ; + ANTENNAMODEL OXIDE3 ; + ANTENNACUMAREARATIO 5000 ; + ANTENNACUMDIFFAREARATIO 8000 ; + ANTENNAMODEL OXIDE3 ; + ANTENNACUMAREARATIO 500 ; + ANTENNACUMDIFFAREARATIO 800 ; + ANTENNAMODEL OXIDE3 ; + ANTENNACUMAREARATIO 300 ; + ANTENNACUMDIFFAREARATIO 600 ; +END M5 + +LAYER implant1 + TYPE IMPLANT ; + WIDTH 0.50 ; + SPACING 0.50 ; + PROPERTY lrp 5.4 ; +END implant1 + +LAYER implant2 + TYPE IMPLANT ; + WIDTH 0.50 ; + SPACING 0.50 ; + PROPERTY lsp "bottom" ; +END implant2 + +LAYER V3 + TYPE CUT ; +END V3 + +LAYER MT + TYPE ROUTING ; + WIDTH 0.9 ; + PITCH 1.8 ; + SPACING 0.9 ; + DIRECTION VERTICAL ; + RESISTANCE RPERSQ 0.0608 ; + CAPACITANCE CPERSQDIST 0.000184 ; + MINSTEP 0.05 STEP ; + MINSTEP 0.05 STEP LENGTHSUM 0.08 ; + MINSTEP 0.04 STEP ; + DIAGMINEDGELENGTH .075 ; +END MT + +layer OVERLAP + TYPE OVERLAP ; + PROPERTY lip 5 lsp "top" ; + PROPERTY lrp 5.5 lsp "bottom" ; +END OVERLAP + +LAYER via12 + TYPE CUT ; + WIDTH 0.20 ; #cuts .20 x .20 squares + SPACING 0.15 CENTERTOCENTER ; #via12 center-to-center spacing is 0.15 + ENCLOSURE BELOW .03 .01 ; #m1; 0.03 on two sides, 0.01 on other sides + ENCLOSURE ABOVE .05 .01 ; #m2; 0.05 on two sides, 0.01 on other sides + ENCLOSURE ABOVE .04 .09 ; #m3; 0.04 on two sides, 0.09 on other sides + PREFERENCLOSURE BELOW 0.06 0.01 ; + PREFERENCLOSURE ABOVE 0.08 0.2 ; + RESISTANCE 10.0 ; #10.0 ohms per cut +END via12 + +LAYER metal1 + TYPE ROUTING ; + WIDTH 0.9 ; + PITCH 1.8 ; + DIRECTION VERTICAL ; + MINSIZE 0.14 0.30 0.5 0.56 0.01 0.05 ; +END metal1 + +LAYER via23 + TYPE CUT ; + WIDTH 0.20 ; #cuts .20 x .20 squares + SPACING 0.15 ; #via23 edge-to-edge spacing is 0.15 + ENCLOSURE .05 .01 ; #m2, m3:0.05 on two sides, 0.01 on other sides + ENCLOSURE .02 .02 WIDTH 1.0 ; #m2 needs 0.02 on all sides if m2 width + #>= 1.0 + #m3 needs 0.02 on all sides if m3 width >= 1.0 + ENCLOSURE .05 .05 WIDTH 2.0 ; #m2 needs 0.05 on all sides if m2 width + #>= 2.0 + #m3 needs 0.02 on all sides if m3 width >= 3.0 + RESISTANCE 10.0 ; #10.0 ohms per cut +END via23 + +LAYER via34 + TYPE CUT ; + WIDTH 0.25 ; #cuts .25 x .25 squares + SPACING 0.10 CENTERTOCENTER ; # 5.7 + ENCLOSURE .05 .01 ; #m3, m4 must meet the enclosure rule + ENCLOSURE .05 0.0 LENGTH 0.7 ; # 5.7 + ENCLOSURE BELOW .07 .07 WIDTH 1.0 ; #m3 needs .07um on all sides if + #the m3 width is >= 1.0um + ENCLOSURE ABOVE .09 .09 WIDTH 1.0 ; #m4 needs .09um on all sides if + #the m4 width is >= 1.0um + ENCLOSURE 0.03 0.03 WIDTH 1.0 EXCEPTEXTRACUT 0.2 ; # 5.7 + RESISTANCE 8.0 ; #8.0 ohms per cut +END via34 + +LAYER cut23 # 5.7 + TYPE CUT ; + SPACING 0.20 SAMENET LAYER cut12 STACK ; + SPACING 0.30 CENTERTOCENTER SAMENET AREA 0.02 ; + SPACING 0.40 AREA 0.5 ; + SPACING 0.10 ; + SPACINGTABLE ORTHOGONAL + WITHIN 0.15 SPACING 0.11 + WITHIN 0.13 SPACING 0.13 + WITHIN 0.11 SPACING 0.15 ; + ARRAYSPACING LONGARRAY CUTSPACING 0.2 + ARRAYCUTS 3 SPACING 1.0 + ARRAYCUTS 4 SPACING 1.5 + ARRAYCUTS 5 SPACING 2.0 ; +END cut23 + +LAYER cut24 # 5.7 + TYPE ROUTING ; + WIDTH 1 ; + PITCH 1.8 ; + DIRECTION HORIZONTAL ; + SPACING 0.10 ; + SPACING 0.12 NOTCHLENGTH 0.15 ; + SPACING 0.14 ENDOFNOTCHWIDTH 0.15 NOTCHSPACING 0.16 NOTCHLENGTH 0.08 ; + ARRAYSPACING WIDTH 2.0 CUTSPACING 0.2 ARRAYCUTS 3 SPACING 1.0 ; +END cut24 + +LAYER cut25 # 5.7 + TYPE ROUTING ; + WIDTH 1 ; + WIREEXTENSION 7 ; + PITCH 1.8 ; + DIRECTION HORIZONTAL ; + SPACINGTABLE + TWOWIDTHS + WIDTH 0.00 0.15 0.20 0.50 1.00 + WIDTH 0.25 PRL 0.0 0.20 0.25 0.50 1.00 + WIDTH 1.50 PRL 1.50 0.50 0.50 0.60 1.00 + WIDTH 3.00 PRL 3.00 1.00 1.00 1.00 1.20 ; +END cut25 + +MAXVIASTACK 4 RANGE m1 m7 ; + +#layer VIRTUAL +# TYPE OVIRTUAL ; +#END VIRTUAL + +VIA IN1X + TOPOFSTACKONLY + FOREIGN IN1X ; + RESISTANCE 2 ; + LAYER RX ; RECT -0.7 -0.7 0.7 0.7 ; RECT 0.0 0 2.1 2.3 ; RECT 5.7 0 95.7 2.3 ; RECT 101.9 0 119.6 2.3 ; + LAYER CUT12 ; + RECT -0.25 -0.25 0.25 0.25 ; + LAYER PC ; + RECT -0.6 -0.6 0.6 0.6 ; + PROPERTY stringProperty "DEFAULT" realProperty 32.33 COUNT 34 ; +END IN1X + +VIA M1_M2 DEFAULT + RESISTANCE 1.5 ; + LAYER M1 ; + RECT MASK 1 -0.6 -0.6 0.6 0.6 ; + LAYER V1 ; + RECT MASK 2 -0.45 -0.45 0.45 0.45 ; + LAYER M2 ; + RECT MASK 3 -0.45 -0.45 0.45 0.45 ; + RECT MASK 1 -0.9 -0.45 0.9 0.45 ; +END M1_M2 + +VIA M2_M3 DEFAULT + RESISTANCE 1.5 ; + LAYER M2 ; + RECT -0.45 -0.9 0.45 0.9 ; + LAYER V2 ; + RECT -0.45 -0.45 0.45 0.45 ; + LAYER M3 ; + RECT -0.45 -0.45 0.45 0.45 ; +END M2_M3 + +VIA M2_M3_PWR GENERATED + RESISTANCE 0.4 ; + LAYER M2 ; + RECT -1.35 -1.35 1.35 1.35 ; + LAYER V2 ; + RECT -1.35 -1.35 -0.45 1.35 ; + RECT 0.45 -1.35 1.35 -0.45 ; + RECT 0.45 0.45 1.35 1.35 ; + LAYER M3 ; + RECT -1.35 -1.35 1.35 1.35 ; +END M2_M3_PWR + +VIA M3_MT DEFAULT + RESISTANCE 1.5 ; + LAYER M3 ; + RECT MASK 1 -0.9 -0.45 0.9 0.45 ; + LAYER V3 ; + RECT MASK 2 -0.45 -0.45 0.45 0.45 ; + LAYER MT ; + RECT MASK 3 -0.45 -0.45 0.45 0.45 ; +END M3_MT + +VIA myBlockVia0 + VIARULE viaName0 ; + CUTSIZE 0.1 0.1 ; + LAYERS metal1 via12 metal2 ; + CUTSPACING 0.1 0.1 ; + ENCLOSURE 0.05 0.01 0.01 0.05 ; + ROWCOL 5 14 ; + PATTERN 2_FF70_3_R4F ; +END myBlockVia0 + +VIA VIACENTER12 + LAYER M1 ; + RECT -4.6 -2.2 4.6 2.2 ; + LAYER V1 ; + RECT -3.1 -0.8 -1.9 0.8 ; + RECT 1.9 -0.8 3.1 0.8 ; + LAYER M2 ; + RECT -4.4 -2.0 4.4 2.0 ; + RESISTANCE 0.24 ; +END VIACENTER12 + +VIA M2_TURN + LAYER M2 ; + RECT -0.45 -0.45 0.45 0.45 ; + RECT -4.4 -2.0 4.4 2.0 ; +END M2_TURN + +VIA myVia23 + LAYER metal2 ; + POLYGON MASK 1 -2.1 -1.0 -0.2 1.0 2.1 1.0 0.2 -1.0 ; + POLYGON MASK 2 -1.1 -2.0 -0.1 2.0 1.1 2.0 0.1 -2.0 ; + POLYGON MASK 3 -3.1 -2.0 -0.3 2.0 3.1 2.0 0.3 -2.0 ; + POLYGON MASK 1 -4.1 -2.0 -0.4 2.0 4.1 2.0 0.4 -2.0 ; + LAYER cut23 ; + RECT MASK 2 -0.4 -0.4 0.4 0.4 ; + POLYGON MASK 3 -2.1 -1.0 -0.2 1.0 2.1 1.0 0.2 -1.0 ; + LAYER metal3 ; + POLYGON MASK 1 -0.2 -1.0 -2.1 1.0 0.2 1.0 2.1 -1.0 ; + LAYER cut33 ; + RECT MASK 1 -0.4 -0.4 0.4 0.4 ; + POLYGON MASK 2 -2.1 -1.0 -0.2 1.0 2.1 1.0 0.2 -1.0 ; + POLYGON MASK 2 -1.1 -2.0 -0.1 2.0 1.1 2.0 0.1 -2.0 ; + RECT MASK 1 -0.5 -0.5 0.5 0.5 ; + RECT MASK 2 -0.3 -0.3 0.3 0.3 ; + POLYGON MASK 3 -3.1 -2.0 -0.3 2.0 3.1 2.0 0.3 -2.0 ; + POLYGON MASK 1 -4.1 -2.0 -0.4 2.0 4.1 2.0 0.4 -2.0 ; + RECT MASK 1 -0.2 -0.2 0.2 0.2 ; + RECT MASK 2 -0.1 -0.1 0.1 0.1 ; +END myVia23 + +VIA myBlockVia + VIARULE DEFAULT ; + CUTSIZE 0.1 0.1 ; + LAYERS metal1 via12 metal2 ; + CUTSPACING 0.1 0.1 ; + ENCLOSURE 0.05 0.01 0.01 0.05 ; + ORIGIN .1 .2 ; + OFFSET 5.1 4.1 3.1 2.1 ; + ROWCOL 1 2 ; +END myBlockVia + +VIARULE VIALIST12 + LAYER M1 ; + DIRECTION VERTICAL ; +# OVERHANG 4.5 ; + WIDTH 9.0 TO 9.6 ; +# METALOVERHANG 0.4 ; + LAYER M2 ; + DIRECTION HORIZONTAL ; + WIDTH 3.0 TO 3.0 ; +# METALOVERHANG 0.3 ; + VIA VIACENTER12 ; + PROPERTY vrsp "new" vrip 1 vrrp 4.5 ; +END VIALIST12 + +VIARULE VIALIST1 + LAYER M1 ; + DIRECTION VERTICAL ; + WIDTH 9.0 TO 9.6 ; +# OVERHANG 4.5 ; +# METALOVERHANG 0.5 ; + LAYER M1 ; + DIRECTION HORIZONTAL ; + WIDTH 3.0 TO 3.0 ; +# OVERHANG 5.5 ; +# METALOVERHANG 0.6 ; +# VIA VIACENTER12 ; +END VIALIST1 + + +VIARULE VIAGEN12 GENERATE + LAYER M1 ; + DIRECTION VERTICAL ; + WIDTH 0.1 TO 19 ; + OVERHANG 1.4 ; + METALOVERHANG 1.0 ; + LAYER M2 ; + DIRECTION HORIZONTAL ; + OVERHANG 1.5 ; + METALOVERHANG 1.0 ; + WIDTH 0.2 TO 1.9 ; + LAYER M3 ; + RECT -0.3 -0.3 0.3 0.3 ; + SPACING 5.6 BY 7.0 ; + RESISTANCE 0.5 ; + PROPERTY vrsp "new" vrip 1 vrrp 5.5 ; +END VIAGEN12 + +VIARULE VIAGEN1 GENERATE + LAYER M1 ; + DIRECTION HORIZONTAL ; + OVERHANG 1.4 ; + METALOVERHANG 1.1 ; + WIDTH 0.1 TO 1.9 ; + LAYER M2 ; + DIRECTION VERTICAL ; + OVERHANG 1.5 ; + METALOVERHANG 1.5 ; + WIDTH 0.2 TO 2.9 ; +# LAYER M3 ; +# RECT ( 1 1 ) ( 1 1 ) ; +# SPACING 0.3 BY 4.5 ; + PROPERTY vrsp "new" vrip 1 vrrp 5.5 ; +END VIAGEN1 + +VIARULE via10 GENERATE + LAYER M1 ; + DIRECTION HORIZONTAL ; + OVERHANG 1.1 ; + WIDTH 0.1 TO 1.9 ; + LAYER M2 ; + DIRECTION VERTICAL ; + OVERHANG 1.2 ; + WIDTH 0.2 TO 2.9 ; + LAYER M3 ; + RECT ( 1 1 ) ( 1 1 ) ; + SPACING 0.3 BY 4.5 ; + PROPERTY vrsp "new" vrip 1 vrrp 5.5 ; +END via10 + +VIARULE via11 GENERATE + LAYER M1 ; + DIRECTION HORIZONTAL ; + WIDTH 0.1 TO 1.9 ; + LAYER M2 ; + DIRECTION VERTICAL ; + WIDTH 0.2 TO 2.9 ; + LAYER M3 ; + RECT ( 1 1 ) ( 1 1 ) ; + SPACING 0.3 BY 4.5 ; + PROPERTY vrsp "new" vrip 1 vrrp 5.5 ; +END via11 + +VIARULE via12 GENERATE DEFAULT + LAYER m1 ; + ENCLOSURE 0.03 0.01 ; # 2 sides need >= 0.03, 2 other sides >= 0.01 + LAYER m2 ; + ENCLOSURE 0.05 0.01 ; # 2 sides need >= 0.05, 2 other sides >= 0.01 + LAYER cut12 ; + RECT -0.1 -0.1 0.1 0.1 ; # cut is .20 by .20 + SPACING 0.40 BY 0.40 ; # center-to-center spacing + RESISTANCE 20 ; # ohms per cut +END via12 + +VIARULE via13 GENERATE + LAYER m1 ; + ENCLOSURE 0.05 0.005 ; # 2 sides need >= 0.05, 2 other sides >= 0.005 + WIDTH 1.0 TO 100.0 ; # for m1 between 1 to 100 um wide + LAYER m2 ; + ENCLOSURE 0.05 0.005 ; # 2 sides need >= 0.05, 2 other sides >= 0.005 + WIDTH 1.0 TO 100.0 ; # for m1 between 1 to 100 um wide + LAYER cut12 ; + RECT -0.07 -0.07 0.07 0.07 ; # cut is .14 by .14 + SPACING 0.16 BY 0.16 ; +END via13 + + +VIARULE via14 + LAYER m1 ; + DIRECTION HORIZONTAL ; +# ENCLOSURE 0.05 0.005 ; 2 sides need >= 0.05, 2 other sides >= 0.005 + WIDTH 1.0 TO 100.0 ; # for m1 between 1 to 100 um wide + LAYER m2 ; +# ENCLOSURE 0.05 0.005 ; 2 sides need >= 0.05, 2 other sides >= 0.005 + DIRECTION VERTICAL ; + WIDTH 1.0 TO 100.0 ; # for m1 between 1 to 100 um wide + via name1 ; +END via14 + +VIARULE TURNM3 GENERATE + LAYER m3 ; + DIRECTION VERTICAL ; + LAYER m3 ; + DIRECTION HORIZONTAL ; +END TURNM3 + +VIARULE VIAGEN3T GENERATE + LAYER m3 ; + DIRECTION HORIZONTAL ; + OVERHANG 0.2 ; + METALOVERHANG 0.0 ; + LAYER v3 ; + RECT -0.45 -0.45 0.45 0.45 ; + SPACING 1.80 by 1.80 ; + LAYER mt ; + DIRECTION VERTICAL ; + OVERHANG 0.2 ; + METALOVERHANG 0.0 ; +END VIAGEN3T + +NONDEFAULTRULE RULE1 + LAYER RX + WIDTH 10.0 ; + SPACING 2.2 ; + WIREEXTENSION 6 ; + RESISTANCE RPERSQ 6.5 ; + CAPACITANCE CPERSQDIST 6.5 ; + EDGECAPACITANCE 6.5 ; + END RX + LAYER PC + WIDTH 10.0 ; + SPACING 2.2 ; + CAPACITANCE CPERSQDIST 6.5 ; + END PC + + LAYER M1 + WIDTH 10.0 ; + SPACING 2.2 ; + RESISTANCE RPERSQ 6.5 ; + END M1 + + LAYER fw + WIDTH 4.800 ; + SPACING 4.800 ; + END fw + + VIA nd1VIARX0 + DEFAULT + TOPOFSTACKONLY + FOREIGN IN1X ; + RESISTANCE 0.2 ; + PROPERTY realProperty 2.3 ; + LAYER RX ; + RECT -3 -3 3 3 ; + LAYER CUT12 ; + RECT -1.0 -1.0 1.0 1.0 ; + LAYER PC ; + RECT -3 -3 3 3 ; + END nd1VIARX0 + + VIA nd1VIA01 + FOREIGN IN1X 5.6 5.3 E ; + RESISTANCE 0.2 ; + LAYER PC ; + RECT -3 -3 3 3 ; + RECT -5 -5 5 5 ; + LAYER CA ; + RECT -1.0 -1.0 1.0 1.0 ; + LAYER M1 ; + RECT -3 -3 3 3 ; + END nd1VIA01 + + VIA nd1VIA12 + RESISTANCE 0.2 ; + LAYER M1 ; + RECT -3 -3 3 3 ; + LAYER V1 ; + RECT -1.0 -1.0 1.0 1.0 ; + LAYER M2 ; + RECT -3 -3 3 3 ; + END nd1VIA12 + + SPACING + SAMENET + CUT01 RX 0.1 STACK ; + END SPACING + PROPERTY ndrsp "single" ndrip 1 ndrrp 6.7 ; +END RULE1 + +NONDEFAULTRULE wide1_5x + LAYER metal1 + WIDTH 1.5 ; # metal1 has 1.5um width + END metal1 + LAYER metal2 + WIDTH 1.5 ; + END metal2 + LAYER metal3 + WIDTH 1.5 ; + END metal3 +END wide1_5x + +NONDEFAULTRULE wide3x + LAYER metal1 + WIDTH 3.0 ; # metal1 has 3.0um width + END metal1 + LAYER metal2 + WIDTH 3.0 ; + END metal2 + LAYER metal3 + WIDTH 3.0 ; + END metal3 + #via12rule and via23rule are used implicitly + MINCUTS cut12 2 ; # at least two-cut vias for cut12 required + MINCUTS cut23 2 ; +END wide3x + +NONDEFAULTRULE analog_rule + HARDSPACING ; # don't let any other signal close to this one + LAYER metal1 + WIDTH 1.5 ; # metal1 has 1.5um width + SPACING 3.0 ; # extra spacing of 3.0um + DIAGWIDTH 5.5 ; + END metal1 + LAYER metal2 + WIDTH 1.5 ; + SPACING 3.0 ; + END metal2 + LAYER metal3 + WIDTH 1.5 ; + SPACING 3.0 ; + END metal3 + #use pre-defined "analog vias" + #the DEFAULT VIARULEs will NOT be inherited + USEVIA via12_fixed_analog_via ; + USEVIA via23_fixed_analog_via ; + USEVIARULE viarule14_fixed_analog ; +END analog_rule + +NONDEFAULTRULE clock1 + LAYER metal1 + WIDTH 1.5 ; # metal1 has 1.5um width + END metal1 + LAYER metal2 + WIDTH 1.5 ; + END metal2 + LAYER metal3 + WIDTH 1.5 ; + END metal3 +END clock1 +NONDEFAULTRULE clock2 + LAYER metal1 + WIDTH 1.5 ; # metal1 has 1.5um width + END metal1 + LAYER metal2 + WIDTH 1.5 ; + END metal2 + LAYER metal3 + WIDTH 1.5 ; + END metal3 +END +NONDEFAULTRULE clock + LAYER metal1 + WIDTH 1.5 ; # metal1 has 1.5um width + END metal1 + LAYER metal2 + WIDTH 1.5 ; + END metal2 + LAYER metal3 + WIDTH 1.5 ; + END metal3 +END clock + +UNIVERSALNOISEMARGIN 0.1 20 ; +EDGERATETHRESHOLD1 0.1 ; +EDGERATETHRESHOLD2 0.9 ; +EDGERATESCALEFACTOR 1.0 ; + +NOISETABLE 1 ; + EDGERATE 20 ; + OUTPUTRESISTANCE 3 ; + VICTIMLENGTH 25 ; + VICTIMNOISE 10 ; +# CORRECTIONFACTOR 3 ; +# OUTPUTRESISTANCE 5 ; +END NOISETABLE + +#CORRECTIONTABLE 1 ; +# EDGERATE 20 ; +# OUTPUTRESISTANCE 3 ; +# VICTIMLENGTH 25 ; +# CORRECTIONFACTOR 10.5 ; +# OUTPUTRESISTANCE 5.4 ; +#END CORRECTIONTABLE + +SPACING + SAMENET CUT01 CA 1.5 ; + SAMENET CA V1 1.5 STACK ; + SAMENET M1 M1 3.5 STACK ; + SAMENET V1 V2 1.5 STACK ; + SAMENET M2 M2 3.5 STACK ; + SAMENET V2 V3 1.5 STACK ; +END SPACING + +MINFEATURE 0.1 0.1 ; + +DIELECTRIC 0.000345 ; + +IRDROP + TABLE DRESHI + 0.0001 -0.7 0.001 -0.8 0.01 -0.9 0.1 -1.0 ; + TABLE DRESLO + 0.0001 -1.7 0.001 -1.6 0.01 -1.5 0.1 -1.3 ; + TABLE DNORESHI + 0.0001 -0.6 0.001 -0.7 0.01 -0.9 0.1 -1.1 ; + TABLE DNORESLO + 0.0001 -1.5 0.001 -1.5 0.01 -1.4 0.1 -1.4 ; +END IRDROP + +SITE COVER + CLASS PAD ; + SYMMETRY R90 ; + SIZE 10.000 BY 10.000 ; +END COVER + +SITE IO + CLASS PAD ; + SIZE 80.000 BY 560.000 ; +END IO + +SITE CORE + CLASS CORE ; + SIZE 0.700 BY 8.400 ; +END CORE + +SITE CORE1 + CLASS CORE ; + SYMMETRY X ; + SIZE 67.2 BY 6 ; +END CORE1 + +SITE MRCORE +# CLASS VIRTUAL ; + CLASS CORE ; + SIZE 3.6 BY 28.8 ; + SYMMETRY Y ; +END MRCORE + +SITE IOWIRED + CLASS PAD ; + SIZE 57.6 BY 432 ; +END IOWIRED + +SITE IMAGE + CLASS CORE ; + ROWPATTERN Fsite1 N Lsite1 N Lsite1 FS ; + SIZE 1 BY 1 ; +END IMAGE + +SITE Fsite + CLASS CORE ; + SIZE 4.0 BY 7.0 ; # 4.0 um wide, 7.0 um high +END Fsite + +SITE Lsite + CLASS CORE ; + SIZE 6.0 BY 7.0 ; # 6.0 um wide, 7.0 um high +END Lsite + +SITE mySite + CLASS CORE ; + ROWPATTERN Fsite N Lsite N Lsite FS ; # a pattern of F + L + flipped L + SIZE 16.0 BY 7.0 ; # width = width(F + L + L) +END mySite + +ARRAY M7E4XXX + SITE CORE -5021.450 -4998.000 N DO 14346 BY 595 STEP 0.700 16.800 ; + SITE CORE -5021.450 -4989.600 FS DO 14346 BY 595 STEP 0.700 16.800 ; + SITE IO 6148.800 5800.000 E DO 1 BY 1 STEP 0.000 0.000 ; + SITE IO 6148.800 3240.000 E DO 1 BY 1 STEP 0.000 0.000 ; + SITE COVER -7315.000 -7315.000 N DO 1 BY 1 STEP 0.000 0.000 ; + SITE COVER 7305.000 7305.000 N DO 1 BY 1 STEP 0.000 0.000 ; + CANPLACE COVER -7315.000 -7315.000 N DO 1 BY 1 STEP 0.000 0.000 ; + CANPLACE COVER -7250.000 -7250.000 N DO 5 BY 1 STEP 40.000 0.000 ; + CANPLACE COVER -7250.000 -7250.000 N DO 5 BY 1 STEP 40.000 0.000 ; + CANNOTOCCUPY CORE -5021.450 -4989.600 FS DO 100 BY 595 STEP 0.700 16.800 ; + CANNOTOCCUPY CORE -5021.450 -4998.000 N DO 100 BY 595 STEP 0.700 16.800 ; + CANNOTOCCUPY CORE -5021.450 -4998.000 N DO 100 BY 595 STEP 0.700 16.800 ; + TRACKS X -6148.800 DO 17569 STEP 0.700 LAYER RX ; + TRACKS Y -6148.800 DO 20497 STEP 0.600 LAYER RX ; + TRACKS Y -6148.800 DO 20497 STEP 0.600 LAYER RX ; + + FLOORPLAN 100% + CANPLACE COVER -7315.000 -7315.000 N DO 1 BY 1 STEP 0.000 0.000 ; + CANPLACE COVER -7250.000 -7250.000 N DO 5 BY 1 STEP 40.000 0.000 ; + CANPLACE CORE -5021.450 -4998.000 N DO 14346 BY 595 STEP 0.700 16.800 ; + CANPLACE CORE -5021.450 -4989.600 FS DO 14346 BY 595 STEP 0.700 16.800 ; + CANNOTOCCUPY CORE -5021.450 -4989.600 FS DO 100 BY 595 STEP 0.700 16.800 ; + CANNOTOCCUPY CORE -5021.450 -4998.000 N DO 100 BY 595 STEP 0.700 16.800 ; + END 100% + GCELLGRID X -6157.200 DO 1467 STEP 8.400 ; + GCELLGRID Y -6157.200 DO 1467 STEP 8.400 ; + GCELLGRID Y -6157.200 DO 1467 STEP 8.400 ; +END M7E4XXX + +MACRO CHK3A +CLASS RING ; + FIXEDMASK ; + SOURCE USER ; + FOREIGN CHKS 0 0 FN ; + ORIGIN 0.9 0.9 ; + EEQ CHK1 ; + LEQ CHK2 ; + SIZE 10.8 BY 28.8 ; +# for testing the lefrWarning.log file +# SITE CORE ; + SYMMETRY X Y R90 ; + SITE CORE ; + POWER 1.0 ; + PROPERTY stringProp "first" integerProp 1 WEIGHT 30.31 ; + + PIN GND + TAPERRULE RULE1 ; + FOREIGN GROUND ( 0 0 ) E ; + FOREIGN CHKS ( 5 4 ) N ; + FOREIGN VCC ( 6 5 ) FE ; + FOREIGN CHK1 ( 7 6 ) W ; + LEQ A ; + DIRECTION INOUT ; + USE GROUND ; + SHAPE ABUTMENT ; + INPUTNOISEMARGIN 6.1 2.3 ; + OUTPUTNOISEMARGIN 5.0 4.6 ; + OUTPUTRESISTANCE 7.4 5.4 ; + POWER 2.0 ; + LEAKAGE 1.0 ; + CAPACITANCE 0.1 ; + RESISTANCE 0.2 ; + PULLDOWNRES 0.5 ; + TIEOFFR 0.8 ; + VHI 5 ; + VLO 0 ; + RISEVOLTAGETHRESHOLD 2.0 ; + FALLVOLTAGETHRESHOLD 2.0 ; + RISETHRESH 22 ; + FALLTHRESH 100 ; + RISESATCUR 4 ; + FALLSATCUR .5 ; + CURRENTSOURCE ACTIVE ; +# ANTENNASIZE 0.6 LAYER RX ; +# NAMETALAREA 3 LAYER M1 ; +# ANTENNAMETALAREA 4 LAYER M2 ; +# ANTENNAMETALLENGTH 5 LAYER M1 ; +# ANTENNAMETALLENGTH 6 LAYER M2 ; + RISESLEWLIMIT 0.01 ; + FALLSLEWLIMIT 0.02 ; + MAXDELAY 21 ; + MAXLOAD 0.1 ; + PROPERTY TYPE "special" intProp 23 realProp 24.25 ; + IV_TABLES LOWT HIGHT ; + + PORT + CLASS CORE ; + LAYER M1 SPACING 0.05 ; + WIDTH 1.0 ; + RECT -0.9 3 9.9 6 ; + VIA 100 300 IN1X ; + END + PORT # 5.7 + CLASS BUMP ; # 5.7 + LAYER M2 SPACING 0.06 ; # 5.7 + END # 5.7 + END GND + PIN VDD + DIRECTION INOUT ; + FOREIGN GROUND STRUCTURE ( 0 0 ) E ; + USE POWER ; + SHAPE ABUTMENT ; + PORT + END + PORT + CLASS NONE ; + LAYER M1 ; + RECT ITERATE -0.9 21 9.9 24 + DO 1 BY 2 STEP 1 1 ; + VIA ITERATE 100 300 nd1VIA12 + DO 1 BY 2 STEP 1 2 ; + END +# ANTENNAMETALAREA 3 LAYER M1 ; +# ANTENNAMETALAREA 4 LAYER M2 ; +# ANTENNAMETALLENGTH 5 LAYER M1 ; +# ANTENNAMETALLENGTH 6 LAYER M2 ; + # Test for combination of both 5.3 & 5.4, which is not allowed + # ANTENNAPARTIALMETALAREA 4 LAYER M1 ; + ANTENNAPARTIALCUTAREA 4.8216 LAYER V1 ; + ANTENNAMODEL OXIDE1 ; + ANTENNAGATEAREA 297.0130 LAYER M2 ; + ANTENNAMODEL OXIDE3 ; + ANTENNAGATEAREA 162.4800 LAYER M2 ; + ANTENNADIFFAREA 5008.4600 LAYER M2 ; + ANTENNAPARTIALMETALAREA 10611.2002 LAYER M2 ; + ANTENNAPARTIALCUTAREA 185.7300 LAYER V2 ; + ANTENNAMODEL OXIDE1 ; + ANTENNAGATEAREA 297.2140 LAYER M3 ; + ANTENNADIFFAREA 5163.8799 LAYER M3 ; + ANTENNAPARTIALMETALAREA 2450.2600 LAYER M3 ; + LEAKAGE 1.0 ; + FALLVOLTAGETHRESHOLD 2.0 ; + RISEVOLTAGETHRESHOLD 2.0 ; + CURRENTSOURCE ACTIVE ; + END VDD + PIN PA3 + DIRECTION INPUT ; + # 5.4 + ANTENNAPARTIALMETALAREA 4 LAYER M1 ; + ANTENNAPARTIALMETALAREA 5 LAYER M2 ; + ANTENNAPARTIALMETALSIDEAREA 5 LAYER M2 ; + ANTENNAPARTIALMETALSIDEAREA 6 LAYER M2 ; + ANTENNAPARTIALMETALSIDEAREA 7 LAYER M2 ; + ANTENNAGATEAREA 1 LAYER M1 ; + ANTENNAGATEAREA 2 ; + ANTENNAGATEAREA 3 LAYER M3 ; + ANTENNADIFFAREA 1 LAYER M1 ; + ANTENNAMAXAREACAR 1 LAYER L1 ; + ANTENNAMAXAREACAR 2 LAYER L2 ; + ANTENNAMAXAREACAR 3 LAYER L3 ; + ANTENNAMAXAREACAR 4 LAYER L4 ; + ANTENNAMAXSIDEAREACAR 1 LAYER L1 ; + ANTENNAMAXSIDEAREACAR 2 LAYER L2 ; + ANTENNAPARTIALCUTAREA 1 ; + ANTENNAPARTIALCUTAREA 2 LAYER M2 ; + ANTENNAPARTIALCUTAREA 3 ; + ANTENNAPARTIALCUTAREA 4 LAYER M4 ; + ANTENNAMAXCUTCAR 1 LAYER L1 ; + ANTENNAMAXCUTCAR 2 LAYER L2 ; + ANTENNAMAXCUTCAR 3 LAYER L3 ; + # Test for combination of both 5.3 & 5.4, which is not allowed + # ANTENNAMETALLENGTH 5 LAYER M1 ; + PORT + LAYER M1 SPACING 0.02 ; + RECT 1.35 -0.45 2.25 0.45 ; + RECT -0.45 -0.45 0.45 0.45 ; + END + PORT + LAYER PC DESIGNRULEWIDTH 0.05 ; + RECT -0.45 12.15 0.45 13.05 ; + END + PORT + LAYER PC ; + RECT -0.45 24.75 0.45 25.65 ; + END + PORT + END + END PA3 + PIN PA0 + DIRECTION INPUT ; + MUSTJOIN PA3 ; + PORT + CLASS NONE ; + LAYER M1 ; + RECT 8.55 8.55 9.45 9.45 ; + RECT 6.75 6.75 7.65 7.65 ; + RECT 6.75 8.55 7.65 9.45 ; + RECT 6.75 10.35 7.65 11.25 ; + END + PORT + CLASS CORE ; + LAYER PC ; + RECT 8.55 24.75 9.45 25.65 ; + END + PORT + LAYER PC ; + RECT 6.75 1.35 7.65 2.25 ; + END + PORT + LAYER PC ; + RECT 6.75 24.75 7.65 25.65 ; + END + PORT + LAYER PC ; + RECT 4.95 1.35 5.85 2.25 ; + END + END PA0 + PIN PA1 + DIRECTION INPUT ; + PORT + LAYER M1 ; + RECT 8.55 -0.45 9.45 0.45 ; + RECT 6.75 -0.45 7.65 0.45 ; + END + PORT + LAYER M1 ; + RECT 8.55 12.15 9.45 13.05 ; + RECT 6.75 12.15 7.65 13.05 ; + RECT 4.95 12.15 5.85 13.05 ; + END + PORT + LAYER PC ; + RECT 4.95 24.75 5.85 25.65 ; + END + PORT + LAYER PC ; + RECT 3.15 24.75 4.05 25.65 ; + END + END PA1 + PIN PA20 + DIRECTION INPUT ; + PORT + LAYER M1 ; + POLYGON 15 35 15 60 65 60 65 35 15 35 ; + END + PORT + LAYER M1 ; + PATH 8.55 12.15 9.45 13.05 ; + END + END PA20 + PIN PA21 + DIRECTION OUTPUT TRISTATE ; + PORT + LAYER M1 ; + POLYGON ITERATE 20 35 20 60 70 60 70 35 DO 1 BY 2 STEP 5 5 ; + END + PORT + LAYER M1 ; + PATH ITERATE 5.55 12.15 10.45 13.05 DO 1 BY 2 STEP 2 2 ; + END + END PA21 + OBS + LAYER M1 SPACING 5.6 ; + RECT 6.6 -0.6 9.6 0.6 ; + RECT 4.8 12 9.6 13.2 ; + RECT 3 13.8 7.8 16.8 ; + RECT 3 -0.6 6 0.6 ; + RECT 3 8.4 6 11.4 ; + RECT 3 8.4 4.2 16.8 ; + RECT -0.6 13.8 4.2 16.8 ; + RECT -0.6 -0.6 2.4 0.6 ; + RECT 6.6 6.6 9.6 11.4 ; + RECT 6.6 6.6 7.8 11.4 ; + END + TIMING + FROMPIN PA21 ; + TOPIN PA20 ; + RISE INTRINSIC .39 .41 1.2 .25 .29 1.8 .67 .87 2.2 + VARIABLE 0.12 0.13 ; + FALL INTRINSIC .24 .29 1.3 .26 .31 1.7 .6 .8 2.1 + VARIABLE 0.11 0.14 ; + RISERS 83.178 90.109 ; + FALLRS 76.246 97.041 ; + RISECS 0.751 0.751 ; + FALLCS 0.751 0.751 ; + RISET0 0.65493 0.65493 ; + FALLT0 0.38 0.38 ; + RISESATT1 0 0 ; + FALLSATT1 0.15 0.15 ; + UNATENESS INVERT ; + END TIMING +END CHK3A + +MACRO INV + CLASS CORE ; + SOURCE BLOCK ; + FOREIGN INVS ; + POWER 1.0 ; + SIZE 67.2 BY 24 ; + SYMMETRY X Y R90 ; + SITE CORE1 ; + + PIN Z DIRECTION OUTPUT ; + USE SIGNAL ; + RISETHRESH 22 ; + FALLTHRESH 100 ; + RISESATCUR 4 ; + FALLSATCUR .5 ; + VLO 0 ; + VHI 5 ; + CAPACITANCE 0.1 ; + MAXDELAY 21 ; + POWER 0.1 ; + ANTENNAPARTIALCUTAREA 4.8216 LAYER V1 ; + ANTENNAMODEL OXIDE1 ; + ANTENNAGATEAREA 297.0130 LAYER M2 ; + ANTENNAMODEL OXIDE2 ; + ANTENNAMODEL OXIDE3 ; + ANTENNAGATEAREA 162.4800 LAYER M2 ; + ANTENNADIFFAREA 5008.4600 LAYER M2 ; + ANTENNAPARTIALMETALAREA 10611.2002 LAYER M2 ; + ANTENNAPARTIALCUTAREA 185.7300 LAYER V2 ; + ANTENNAMODEL OXIDE1 ; + ANTENNAGATEAREA 297.2140 LAYER M3 ; + ANTENNADIFFAREA 5163.8799 LAYER M3 ; + ANTENNAPARTIALMETALAREA 2450.2600 LAYER M3 ; + PORT + LAYER M2 ; + PATH 30.8 9 42 9 ; + END + END Z + + PIN A DIRECTION INPUT ; + USE ANALOG ; + RISETHRESH 22 ; + FALLTHRESH 100 ; + RISESATCUR 4 ; + FALLSATCUR .5 ; + VLO 0 ; + VHI 5 ; + CAPACITANCE 0.08 ; + MAXDELAY 21 ; + PORT + LAYER M1 ; + PATH 25.2 15 ; + END + END A + + PIN VDD DIRECTION INOUT ; + SHAPE ABUTMENT ; + POWER 0.1 ; + ANTENNAPARTIALCUTAREA 4.8216 LAYER V1 ; + ANTENNAMODEL OXIDE1 ; + ANTENNAGATEAREA 297.0130 LAYER M2 ; + ANTENNAMODEL OXIDE3 ; + ANTENNAGATEAREA 162.4800 LAYER M2 ; + ANTENNADIFFAREA 5008.4600 LAYER M2 ; + ANTENNAPARTIALMETALAREA 10611.2002 LAYER M2 ; + ANTENNAPARTIALCUTAREA 185.7300 LAYER V2 ; + ANTENNAMODEL OXIDE1 ; + ANTENNAGATEAREA 297.2140 LAYER M3 ; + ANTENNADIFFAREA 5163.8799 LAYER M3 ; + ANTENNAPARTIALMETALAREA 2450.2600 LAYER M3 ; + ANTENNAMODEL OXIDE2 ; + PORT + LAYER M1 ; + WIDTH 5.6 ; + PATH 50.4 2.8 50.4 21.2 ; + END + END VDD + + PIN VSS DIRECTION INOUT ; + SHAPE ABUTMENT ; + POWER 0.1 ; + PORT + LAYER M1 ; + WIDTH 5.6 ; + PATH 16.8 2.8 16.8 21.2 ; + END + END VSS + + TIMING + FROMPIN A ; + TOPIN Z ; + RISE INTRINSIC .39 .41 1.2 .25 .29 1.8 .67 .87 2.2 + VARIABLE 0.12 0.13 ; + FALL INTRINSIC .24 .29 1.3 .26 .31 1.7 .6 .8 2.1 + VARIABLE 0.11 0.14 ; + RISERS 83.178 90.109 ; + FALLRS 76.246 97.041 ; + RISECS 0.751 0.751 ; + FALLCS 0.751 0.751 ; + RISET0 0.65493 0.65493 ; + FALLT0 0.38 0.38 ; + RISESATT1 0 0 ; + FALLSATT1 0.15 0.15 ; + UNATENESS INVERT ; + END TIMING + + OBS + LAYER M1 DESIGNRULEWIDTH 4.5 ; + WIDTH 0.1 ; + RECT MASK 2 24.1 1.5 43.5 16.5 ; + RECT MASK 2 ITERATE 24.1 1.5 43.5 16.5 + DO 2 BY 1 STEP 20.0 0 ; + PATH MASK 3 ITERATE 532.0 534 1999.2 534 + DO 1 BY 2 STEP 0 1446 ; + VIA ITERATE MASK 123 470.4 475 VIABIGPOWER12 + DO 2 BY 2 STEP 1590.4 1565 ; + PATH MASK 3 532.0 534 1999.2 534 ; + PATH MASK 3 532.0 1980 1999.2 1980 ; + VIA MASK 103 470.4 475 VIABIGPOWER12 ; + VIA MASK 132 2060.8 475 VIABIGPOWER12 ; + VIA MASK 112 470.4 2040 VIABIGPOWER12 ; + VIA MASK 123 2060.8 2040 VIABIGPOWER12 ; + RECT 44.1 1.5 63.5 16.5 ; + END + + DENSITY + LAYER metal1 ; + RECT 0 0 100 100 45.5 ; #rec from (0,0) to (100,100), density of 45.5% + RECT 100 0 200 100 42.2 ; + LAYER metal2 ; + RECT 0 0 250 140 20.5 ; + RECT 1 1 250 140 20.5 ; + RECT 2 2 250 140 20.5 ; + LAYER metal3 ; + RECT 10 10 40 40 4.5 ; + END +END INV + +MACRO INV_B + EEQ INV ; + CLASS CORE SPACER ; + FOREIGN INVS ( 4 5 ) ; + FOREIGN INV1 ( 5 6 ) S ; + FOREIGN INV2 ( 6 7 ) N ; + FOREIGN INV3 ( 7 8 ) ; + POWER 1.0 ; + SIZE 67.2 BY 24 ; + SYMMETRY X Y R90 ; + SITE CORE1 ; + PIN Z DIRECTION OUTPUT ; + USE CLOCK ; + RISETHRESH 22 ; + FALLTHRESH 100 ; + RISESATCUR 4 ; + FALLSATCUR .5 ; + VLO 0 ; + VHI 5 ; + CAPACITANCE 0.1 ; + MAXDELAY 21 ; + POWER 0.1 ; + PORT + LAYER M1 ; + WIDTH 1 ; + PATH MASK 2 ( -0.6 -0.6 ) ( 0.6 -0.6 ) ( 0.7 -0.6 ) ; + LAYER M2 ; + WIDTH 1 ; + RECT MASK 1 ( -0.6 -0.6 ) ( 0.6 0.6 ) ; + LAYER M3 ; + WIDTH 1 ; + RECT MASK 2 ITERATE ( -0.6 -0.6 ) ( 0.6 0.6 ) + DO 1 BY 2 STEP 2 1 ; + LAYER M4 ; + PATH MASK 1 30.8 9 42 9 ; + VIA MASK 103 470.4 475 VIABIGPOWER12 ; + VIA MASK 130 2060.8 475 VIABIGPOWER12 ; + VIA MASK 113 470.4 2040 VIABIGPOWER12 ; + VIA MASK 121 2060.8 2040 VIABIGPOWER12 ; + END + END Z + + PIN A DIRECTION FEEDTHRU ; + USE SIGNAL ; + RISETHRESH 22 ; + FALLTHRESH 100 ; + RISESATCUR 4 ; + FALLSATCUR .5 ; + VLO 0 ; + VHI 5 ; + CAPACITANCE 0.08 ; + MAXDELAY 21 ; + PORT + LAYER M1 ; + PATH 25.2 15 ; + END + END A + + PIN VDD DIRECTION INOUT ; + SHAPE ABUTMENT ; + POWER 0.1 ; + PORT + LAYER M1 ; + WIDTH 5.6 ; + PATH 50.4 2.8 50.4 21.2 ; + END + END VDD + + PIN VSS DIRECTION INOUT ; + SHAPE ABUTMENT ; + POWER 0.1 ; + PORT + LAYER M1 ; + WIDTH 5.6 ; + PATH 16.8 2.8 16.8 21.2 ; + END + END VSS + + TIMING + FROMPIN A ; + TOPIN Z ; + RISE INTRINSIC .39 .41 1.2 .25 .29 1.8 .67 .87 2.2 + VARIABLE 0.12 0.13 ; + FALL INTRINSIC .24 .29 1.3 .26 .31 1.7 .6 .8 2.1 + VARIABLE 0.11 0.14 ; + RISERS 83.178 90.109 ; + FALLRS 76.246 97.041 ; + RISECS 0.751 0.751 ; + FALLCS 0.751 0.751 ; + RISET0 0.65493 0.65493 ; + FALLT0 0.38 0.38 ; + RISESATT1 0 0 ; + FALLSATT1 0.15 0.15 ; + UNATENESS INVERT ; + END TIMING + + OBS + LAYER M1 ; + RECT 24.1 1.5 43.5 16.5 ; + END +END INV_B + +MACRO DFF3 + CLASS CORE ANTENNACELL ; + FOREIGN DFF3S ; + POWER 4.0 ; + SIZE 67.2 BY 210 ; + SYMMETRY X Y R90 ; + SITE CORE 34 54 FE DO 30 BY 3 STEP 1 1 ; + SITE CORE1 21 68 S DO 30 BY 3 STEP 2 2 ; + + PIN Q DIRECTION OUTPUT ; + USE SIGNAL ; + RISETHRESH 22 ; + FALLTHRESH 100 ; + RISESATCUR 4 ; + FALLSATCUR .5 ; + VLO 0 ; + VHI 5 ; + CAPACITANCE 0.12 ; + MAXDELAY 20 ; + POWER 0.4 ; + PORT + LAYER M2 ; + PATH 19.6 99 47.6 99 ; + END + END Q + + PIN QN DIRECTION OUTPUT ; + USE SIGNAL ; + RISETHRESH 22 ; + FALLTHRESH 100 ; + RISESATCUR 4 ; + FALLSATCUR .5 ; + VLO 0 ; + VHI 5 ; + CAPACITANCE 0.11 ; + MAXDELAY 20 ; + POWER 0.4 ; + PORT + LAYER M2 ; + PATH MASK 1 25.2 123 42 123 ; + RECT MASK 2 24.1 1.5 43.5 208.5 ; + END + END QN + + PIN D DIRECTION INPUT ; + USE SIGNAL ; + RISETHRESH 22 ; + FALLTHRESH 100 ; + RISESATCUR 4 ; + FALLSATCUR .5 ; + VLO 0 ; + VHI 5 ; + CAPACITANCE 0.13 ; + MAXDELAY 20 ; + POWER 0.4 ; + PORT + LAYER M1 ; + PATH 30.8 51 ; + END + END D + + PIN G DIRECTION INPUT ; + USE SIGNAL ; + RISETHRESH 22 ; + FALLTHRESH 100 ; + RISESATCUR 4 ; + FALLSATCUR .5 ; + VLO 0 ; + VHI 5 ; + CAPACITANCE 0.11 ; + MAXDELAY 20 ; + POWER 0.4 ; + PORT + LAYER M1 ; + PATH 25.2 3 ; + END + END G + + PIN CD DIRECTION INPUT ; + USE CLOCK ; + RISETHRESH 22 ; + FALLTHRESH 100 ; + RISESATCUR 4 ; + FALLSATCUR .5 ; + VLO 0 ; + VHI 5 ; + CAPACITANCE 0.1 ; + MAXDELAY 20 ; + POWER 0.4 ; + PORT + LAYER M1 ; + PATH 36.4 75 ; + END + END CD + + PIN VDD DIRECTION INOUT ; + SHAPE RING ; + POWER 0.4 ; + PORT + LAYER M1 ; + WIDTH 5.6 ; + PATH 50.4 2.8 50.4 207.2 ; + END + END VDD + + PIN VSS DIRECTION INOUT ; + SHAPE FEEDTHRU ; + POWER 0.4 ; + PORT + LAYER M1 ; + WIDTH 5.6 ; + PATH 16.8 2.8 16.8 207.2 ; + END + END VSS + + TIMING + FROMPIN D ; + TOPIN Q ; + RISE INTRINSIC .51 .6 1.4 .37 .45 1.7 .6 .81 2.1 + VARIABLE 0.06 0.1 ; + FALL INTRINSIC 1 1.2 1.4 1.77 1.85 1.8 .56 .81 2.4 + VARIABLE 0.08 0.09 ; + RISERS 41.589 69.315 ; + FALLRS 55.452 62.383 ; + RISECS 0.113 0.113 ; + FallCS 0.113 0.113 ; + RISET0 0.023929 0.023929 ; + FALLT0 0.38 0.38 ; + RISESATT1 0 0 ; + FALLSATT1 0.15 0.15 ; + UNATENESS NONINVERT ; + END TIMING + + OBS + LAYER M1 DESIGNRULEWIDTH 0.15 ; + RECT 24.1 1.5 43.5 208.5 ; + PATH 8.4 3 8.4 123 ; + PATH 58.8 3 58.8 123 ; + PATH 64.4 3 64.4 123 ; + END + + DENSITY + LAYER metal4 ; + RECT 24.1 1.5 43.5 208.5 5.5 ; + END +END DFF3 + +MACRO BUF1 + CLASS ENDCAP BOTTOMLEFT ; + PIN IN + ANTENNAGATEAREA 1 ; + ANTENNAGATEAREA 3 ; + ANTENNADIFFAREA 0 ; + ANTENNAMODEL OXIDE2 ; + ANTENNAGATEAREA 2 ; + ANTENNAGATEAREA 4 ; + ANTENNADIFFAREA 0 ; + END IN + PIN IN2 + ANTENNAGATEAREA 1 ; + END IN2 + PIN IN3 + SHAPE ABUTMENT ; + END IN3 + PIN GND + USE GROUND ; + PORT + LAYER metal1 ; + POLYGON 0 0 0 1.0 1.0 0 2.0 2.0 2.0 0 ; + RECT 2.0 3.0 4.0 5.0 ; + END + END GND + OBS + LAYER metal2 EXCEPTPGNET ; # 5.7 + POLYGON 0 0 0 1.0 1.0 0 2.0 2.0 2.0 0 ; + RECT 2.0 3.0 4.0 5.0 ; + END +END BUF1 + +MACRO DFF4 + CLASS COVER BUMP ; + FOREIGN DFF3S ; + POWER 4.0 ; +END DFF4 + +MACRO DFF5 + CLASS COVER ; + FOREIGN DFF3S ; +END DFF5 + +MACRO mydriver + CLASS PAD AREAIO ; + FOREIGN DFF3S ; +END mydriver + +MACRO myblackbox + CLASS BLOCK BLACKBOX ; + FOREIGN DFF3S ; +END myblackbox + +MACRO FWHSQCN690V15 + CLASS CORE ; + FOREIGN FWHSQCN690 0.00 0.00 ; + SIZE 15.36 BY 4.80 ; + SYMMETRY Y X ; + ORIGIN 0.0 0.0 ; + SITE CORE ; + PIN R DIRECTION INPUT ; + CAPACITANCE 0.004872 ; + USE SIGNAL ; + PORT + LAYER a1sig ; + RECT 11.43 0.80 11.71 1.20 ; + RECT 9.94 0.80 11.43 1.04 ; + LAYER a1sig ; + RECT 9.49 0.80 9.71 0.82 ; + RECT 9.71 0.80 9.93 1.04 ; + RECT 9.92 0.80 9.94 1.04 ; + RECT 9.00 2.06 9.72 2.08 ; + RECT 9.39 0.80 9.47 1.78 ; + RECT 9.46 0.80 9.48 1.78 ; + RECT 9.47 0.80 9.49 1.77 ; + RECT 9.48 0.81 9.72 2.07 ; + RECT 9.00 1.77 9.48 2.07 ; + END + END R + PIN SI DIRECTION INPUT ; + CAPACITANCE 0.002213 ; + USE SIGNAL ; + PORT + LAYER a1sig ; +# RECT 4.00 2.07 4.20 2.36 ; +# RECT 4.19 2.07 4.21 2.36 ; +# RECT 4.21 2.07 4.44 2.25 ; +# RECT 4.21 2.24 4.44 2.26 ; +# RECT 4.20 2.25 4.44 3.03 ; + END + END SI + PIN SM DIRECTION INPUT ; + CAPACITANCE 0.002307 ; + USE SIGNAL ; + PORT + LAYER a1sig ; + RECT 0.82 3.51 1.08 3.89 ; + LAYER a1sig ; + RECT 0.36 3.51 0.59 3.69 ; + RECT 0.36 3.68 0.59 3.70 ; + RECT 0.59 3.51 0.81 3.89 ; + RECT 0.59 3.51 0.81 3.89 ; + RECT 0.80 3.51 0.82 3.89 ; + RECT 0.36 3.69 0.60 3.99 ; + LAYER a1sig ; + END + END SM + PIN T DIRECTION INPUT ; + CAPACITANCE 0.002260 ; + USE SIGNAL ; + PORT + LAYER a1sig ; +# RECT 5.14 3.51 5.15 3.99 ; + LAYER a1sig ; + RECT 4.85 3.51 4.91 3.70 ; + RECT 4.91 3.51 5.13 3.99 ; + RECT 5.12 3.51 5.14 3.99 ; + RECT 4.20 3.69 4.92 3.99 ; + END + END T + OBS + LAYER a1sig SPACING 0 ; + RECT 0.11 0.74 0.81 3.47 ; + RECT 0.80 0.74 0.82 3.47 ; + RECT 0.82 0.74 2.54 4.06 ; + RECT 2.54 0.74 3.70 3.47 ; + RECT 3.70 0.74 3.98 4.06 ; + RECT 3.98 0.74 5.14 3.47 ; + RECT 5.14 0.74 8.78 4.06 ; + RECT 8.78 0.74 9.94 1.54 ; + RECT 8.78 1.53 9.94 1.55 ; + RECT 8.78 2.29 9.94 4.06 ; + RECT 9.94 0.74 14.06 4.06 ; + RECT 14.06 0.74 15.24 2.51 ; + RECT 15.23 0.74 15.25 2.51 ; + RECT 14.06 3.25 15.25 4.05 ; + RECT 14.06 4.04 15.25 4.06 ; + END +END FWHSQCN690V15 + +MACRO mysoft + CLASS BLOCK SOFT ; + FOREIGN DFF3S ; + OBS + LAYER a1sig DESIGNRULEWIDTH 0 ; + RECT 0.11 0.74 0.81 3.47 ; + END +END mysoft + +MACRO mycorewelltap + CLASS CORE WELLTAP ; + FOREIGN DFF3S ; +END mycorewelltap + +MACRO myTest + CLASS CORE ; + SIZE 10.0 BY 14.0 ; #uses two F and one L site, is F+L wide,and double height + SYMMETRY X ; #can flip about X-axis + SITE Fsite 0 0 N ; #the lower, left Fsite at 0,0 + SITE Fsite 0 7.0 FS ; #the flipped-south Fsite above the first Fsite at 0,7 + SITE Lsite 4.0 0 N ; #the Lsite to the right of the first Fsite at 4,0 + SITE Lsite 0 0 S DO 2 BY 1 STEP 4 5 ; + SITE Fsite 4.0 0 E ; + SITE Fsite ; + SITE Lsite 0.3 0 S DO 2 BY 1 STEP 4 5 ; + SITE Fsite 0 0 N DO 2 BY 1 STEP 4.0 0 ; +END myTest + +MACRO myMac + CLASS CORE ; + SIZE 10.0 BY 14.0 ; + SYMMETRY X ; + PIN In1 + USE SIGNAL ; + SUPPLYSENSITIVITY vddpin1 ; #if in1 is 1'b1, use net connected to vddpin1 + #note, no GROUNDSENSITIVITY is needed because + #only one ground pin exists, so 1'b0 implicitly + #means net from pin gndpin + CAPACITANCE 0.12 ; + MAXDELAY 20 ; + END In1 + PIN vddpin1 + USE SIGNAL ; + CAPACITANCE 0.11 ; + MAXDELAY 20 ; + NETEXPR "power1 VDD1" ; #if power1 is defined in the netlist, use it to + #find the net connection, else use net VDD1 + POWER 0.4 ; + END vddpin1 + PIN vddpin2 + USE SIGNAL ; + MAXDELAY 20 ; + NETEXPR "power2 VDD2" ; #if power2 is defined in the netlist, use it to + #find the net connection, else use net VDD2 + POWER 0.4 ; + END vddpin2 + PIN gndpin + USE SIGNAL ; + NETEXPR "gnd1 GND" ; #if gnd1 is defined in the netlist, use it to + #find the net connection, else use net GND + MAXDELAY 20 ; + POWER 0.4 ; + END gndpin + PIN In2 + USE SIGNAL ; + GROUNDSENSITIVITY gndpin ; + POWER 0.6 ; + END In2 +END myMac + +ANTENNAINPUTGATEAREA 45 ; +ANTENNAINOUTDIFFAREA 65 ; +ANTENNAOUTPUTDIFFAREA 55 ; + +#INPUTPINANTENNASIZE 1 ; +#OUTPUTPINANTENNASIZE -1 ; +#INOUTPINANTENNASIZE -1 ; + +BEGINEXT "SIGNATURE" + CREATOR "CADENCE" + DATE "04/14/98" +ENDEXT + +#END LIBRARY This is optional in 5.6 diff --git a/tests/test_leflib.py b/tests/test_leflib.py new file mode 100644 index 0000000..2a01575 --- /dev/null +++ b/tests/test_leflib.py @@ -0,0 +1,146 @@ +import sc_leflib + +import os + + +def test_leflib(scroot): + path = os.path.join(scroot, + 'third_party', + 'pdks', + 'skywater', + 'skywater130', + 'pdk', + 'v0_0_2', + 'apr', + 'sky130_fd_sc_hd.tlef') + + data = sc_leflib.parse(path) + assert data['version'] == 5.7 + + +def test_leflib_garbage(): + assert sc_leflib.parse('asdf') is None + + +def test_leflib_complete(datadir): + # This file contains nonsense and some things that are technically illegal, + # but good coverage of what you might see in a LEF. I've left comments where + # the contents of `data` don't perfectly match the LEF contents, with + # explanations as to why. + path = os.path.join(datadir, 'complete.5.8.lef') + data = sc_leflib.parse(path) + + assert data['version'] == 5.8 + assert data['fixedmask'] is True + # There's a "USEMINSPACING PIN" statement, but it gets skipped since it's + # obsolete syntax in 5.8 + assert data['useminspacing'] == {'OBS': 'OFF'} + # There are two CLEARANCEMEASURE statements, we only take the second one + assert data['clearancemeasure'] == 'MAXXY' + assert data['busbitchars'] == '<>' + assert data['dividerchar'] == ':' + assert data['units'] == { + 'capacitance': 10.0, + 'current': 10000.0, + 'database': 20000.0, + 'frequency': 10.0, + 'power': 10000.0, + 'resistance': 10000.0, + 'time': 100.0, + 'voltage': 1000.0 + } + assert data['manufacturinggrid'] == 3.5 + + assert len(data['layers']) == 26 + assert data['layers']['POLYS']['type'] == 'MASTERSLICE' + + cut01 = data['layers']['CUT01'] + assert cut01['type'] == 'CUT' + assert cut01['offset'] == (0.5, 0.6) + assert cut01['pitch'] == (1.2, 1.3) + + rx = data['layers']['RX'] + assert rx['type'] == 'ROUTING' + assert rx['pitch'] == 1.8 + assert rx['offset'] == 0.9 + assert rx['width'] == 1 + assert rx['direction'] == 'HORIZONTAL' + + assert data['maxviastack'] == {'value': 4, 'range': {'bottom': 'm1', 'top': 'm7'}} + assert data + + # 11 viarules defined, but two are skipped because they are "turn-vias" + # (obsolete in 5.8) + assert len(data['viarules']) == 9 + + vialist12 = data['viarules']['VIALIST12'] + assert len(vialist12['layers']) == 2 + assert vialist12['layers'][0] == { + 'name': 'M1', + 'direction': 'VERTICAL', + 'width': {'min': 9.0, 'max': 9.6} + } + assert vialist12['layers'][1] == { + 'name': 'M2', + 'direction': 'HORIZONTAL', + 'width': {'min': 3.0, 'max': 3.0} + } + assert vialist12['vias'] == ['VIACENTER12'] + + via12 = data['viarules']['via12'] + assert via12['generate'] is True + assert via12['default'] is True + + assert len(via12['layers']) == 3 + assert via12['layers'][0] == { + 'name': 'm1', + 'enclosure': {'overhang1': 0.03, 'overhang2': 0.01} + } + assert via12['layers'][1] == { + 'name': 'm2', + 'enclosure': {'overhang1': 0.05, 'overhang2': 0.01} + } + assert via12['layers'][2] == { + 'name': 'cut12', + 'spacing': {'x': 0.4, 'y': 0.4}, + 'rect': (-0.1, -0.1, 0.1, 0.1), + 'resistance': 20 + } + + assert len(data['sites']) == 10 + cover = data['sites']['COVER'] + assert cover['class'] == 'PAD' + assert cover['symmetry'] == ['R90'] + assert cover['size'] == {'width': 10, 'height': 10} + + assert len(data['macros']) == 14 + chk3a = data['macros']['CHK3A'] + assert chk3a['size'] == {'width': 10.8, 'height': 28.8} + assert len(chk3a['pins']) == 7 + vdd = chk3a['pins']['VDD'] + assert len(vdd['ports']) == 2 + port = vdd['ports'][1] + assert port['class'] == 'NONE' + assert len(port['layer_geometries']) == 1 + geo = port['layer_geometries'][0] + assert geo['layer'] == 'M1' + assert geo['shapes'] == [{ + 'rect': (-0.9, 21, 9.9, 24), + 'mask': 0, + 'iterate': { + 'num_x': 1, + 'num_y': 2, + 'step_x': 1, + 'step_y': 1 + } + }] + + assert len(chk3a['obs']) == 1 + obs = chk3a['obs'][0][0] + assert obs['layer'] == 'M1' + assert obs['spacing'] == 5.6 + assert len(obs['shapes']) == 10 + assert obs['shapes'][0] == { + 'rect': (6.6, -0.6, 9.6, 0.6), + 'mask': 0 + }