Creating a GitHub Action to run DRC checks on cells. This GitHub Action uses Magic to run the SKY130 design rules pulled from OpenPDKs on all the cells in a standard library. See the `README.rst` for instructions on how to use. Signed-off-by: Tim 'mithro' Ansell <tansell@google.com> Signed-off-by: Mohamed Gaber <mohamed.gaber@efabless.com>
diff --git a/.github/workflows/build-docker-image-run-drc-for-cell-gds-using-magic.yml b/.github/workflows/build-docker-image-run-drc-for-cell-gds-using-magic.yml new file mode 100644 index 0000000..c1d7b9c --- /dev/null +++ b/.github/workflows/build-docker-image-run-drc-for-cell-gds-using-magic.yml
@@ -0,0 +1,52 @@ +# Copyright 2021 SkyWater PDK Authors +# +# 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 +# +# https://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. +# +# SPDX-License-Identifier: Apache 2.0 + +name: Build Docker Image for Run DRC for cell GDS (using Magic) Action + +on: + workflow_dispatch: + push: + +jobs: + prebuild-magic-gds-drc: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Set Action Name + run: echo "ACTION_NAME=run-drc-for-cell-gds-using-magic" >> $GITHUB_ENV + + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ github.token }} + + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: ${{ env.ACTION_NAME }} + file: ${{ env.ACTION_NAME }}/Dockerfile + push: true + tags: ghcr.io/${{ github.repository }}-${{ env.ACTION_NAME }}:latest + + - name: Image digest + run: echo ${{ steps.docker_build.outputs.digest }}
diff --git a/.gitignore b/.gitignore index a81c8ee..b530fea 100644 --- a/.gitignore +++ b/.gitignore
@@ -136,3 +136,6 @@ # Cython debug symbols cython_debug/ + +# IDEs +.vscode/
diff --git a/AUTHORS b/AUTHORS index ea70201..de14df6 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -7,7 +7,7 @@ # Companies Google LLC -efabless corporation +Efabless Corporation The American University in Cairo # Individuals
diff --git a/environment.yml b/environment.yml index 2a7b5da..b740766 100644 --- a/environment.yml +++ b/environment.yml
@@ -23,3 +23,4 @@ # Packages installed from PyPI - pip: - -r file:requirements.txt + - -r file:run-drc-for-cell-gds-using-magic/requirements.txt
diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..4735410 --- /dev/null +++ b/pytest.ini
@@ -0,0 +1,2 @@ +[pytest] +addopts = --doctest-modules --ignore-glob=env --ignore-glob=third_party
diff --git a/requirements.txt b/requirements.txt index 58d13b0..5dc7f8b 100644 --- a/requirements.txt +++ b/requirements.txt
@@ -1,4 +1,6 @@ +# Python code testing + linting flake8 +pytest # rst_include tool as GitHub doesn't support `.. include::` when rendering # previews.
diff --git a/run-drc-for-cell-gds-using-magic/Dockerfile b/run-drc-for-cell-gds-using-magic/Dockerfile new file mode 100644 index 0000000..ab321e8 --- /dev/null +++ b/run-drc-for-cell-gds-using-magic/Dockerfile
@@ -0,0 +1,70 @@ +# Copyright 2021 SkyWater PDK Authors +# +# 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 +# +# https://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. +# +# SPDX-License-Identifier: Apache-2.0 + +FROM ubuntu:20.04 + +ENV DEBIAN_FRONTEND noninteractive + +RUN apt-get update +RUN apt-get install -y build-essential git curl python3 python3-pip tcl-dev tk-dev csh libcairo2-dev + +WORKDIR /build + +# Setup Magic +ENV MAGIC_TAG 8.3.160 +RUN \ + curl -L https://github.com/RTimothyEdwards/magic/archive/refs/tags/${MAGIC_TAG}.tar.gz -o /build/magic.tar.gz \ + && sha256sum /build/magic.tar.gz \ + && mkdir /build/magic \ + && tar -xzC /build/magic --strip-components=1 -f /build/magic.tar.gz \ + && cd /build/magic \ + && ./configure \ + && make -j$(nproc) \ + && make install \ + && rm -rf /build/magic* + +# Setup Python Dependencies +WORKDIR /build +COPY ./requirements.txt /build/requirements.txt +RUN \ + cat /build/requirements.txt \ + && python3 -m pip install -r /build/requirements.txt --progress-bar off \ + && rm -rf ~/.cache/pip + +# OpenPDKs +## Tag must exist on both https://github.com/efabless/open_pdk_techfiles +## and https://github.com/RTimothyEdwards/open_pdks +ENV OPEN_PDKS_TAG 1.0.159 + +## Download run_standard_drc +RUN \ + curl -L https://raw.githubusercontent.com/RTimothyEdwards/open_pdks/${OPEN_PDKS_TAG}/sky130/custom/scripts/run_standard_drc.py -o /usr/bin/run_standard_drc.py \ + && sha256sum /usr/bin/run_standard_drc.py + +## Download Precompiled OpenPDKs Magic Tech Files +ENV PDK_ROOT /share/pdk/sky130A +RUN \ + curl -L https://github.com/efabless/open_pdk_techfiles/releases/download/${OPEN_PDKS_TAG}/sky130A_tech_magic.tar.xz -o /build/sky130A_tech_magic.tar.xz \ + && sha256sum /build/sky130A_tech_magic.tar.xz \ + && mkdir -p ${PDK_ROOT}/libs.tech/magic \ + && tar -xC ${PDK_ROOT}/libs.tech/magic -f /build/sky130A_tech_magic.tar.xz \ + && rm -rf /build/* + +# Copy Entry Point +COPY ./run_all_drc.py /usr/bin/run_all_drc.py +RUN chmod +x /usr/bin/run_all_drc.py + +ENTRYPOINT ["/usr/bin/run_all_drc.py"]
diff --git a/run-drc-for-cell-gds-using-magic/Makefile b/run-drc-for-cell-gds-using-magic/Makefile new file mode 100644 index 0000000..fb408e8 --- /dev/null +++ b/run-drc-for-cell-gds-using-magic/Makefile
@@ -0,0 +1,24 @@ +# Copyright 2021 SkyWater PDK Authors +# +# 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 +# +# https://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. +# +# SPDX-License-Identifier: Apache-2.0 + +TOP_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))/..) + +README.rst: README.src.rst $(TOP_DIR)/docs/*.rst $(TOP_DIR)/Makefile + make -C $(TOP_DIR) run-drc-for-cell-gds-using-magic/README.rst + +# Redirect everything to the top directory by default. +%: + make -C $(TOP_DIR) $@
diff --git a/run-drc-for-cell-gds-using-magic/README.rst b/run-drc-for-cell-gds-using-magic/README.rst new file mode 100644 index 0000000..dd4d8e9 --- /dev/null +++ b/run-drc-for-cell-gds-using-magic/README.rst
@@ -0,0 +1,145 @@ +``skywater-pdk-actions`` - ``run-drc-for-cell-gds-using-magic`` +============================================================= + +This GitHub action runs Design Rule Checks on all GDS files inside the /cells +directory. + +Usage +===== + +Add this to any push, PR or manual dispatch workflow: + +.. code:: yml + + steps: + - uses: actions/checkout@v2 + + - name: Run Magic DRC + uses: docker://ghcr.io/google/skywater-pdk-actions-run-drc-for-cell-gds-using-magic:latest + with: + args: --acceptable-errors-file /dev/null --match-directories . --known-bad '' + +Check the Python file for more documentation on arguments. + +How to Contribute +================= + +We'd love to accept your patches and contributions to this project. +There are just a few small guidelines you need to follow. + +Contributor License Agreement +----------------------------- + +Contributions to this project must be accompanied by a Contributor +License Agreement. You (or your employer) retain the copyright to your +contribution; this simply gives us permission to use and redistribute +your contributions as part of the project. Head over to +https://cla.developers.google.com/ to see your current agreements on +file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already +submitted one (even if it was for a different project), you probably +don't need to do it again. + +Code reviews +------------ + +All submissions, including submissions by project members, require +review. We use GitHub pull requests for this purpose. Consult `GitHub +Help <https://help.github.com/articles/about-pull-requests/>`__ for more +information on using pull requests. + +Community Guidelines +-------------------- + +This project follows `Google's Open Source Community +Guidelines <https://opensource.google/conduct/>`__. + +At Google, we recognize and celebrate the creativity and collaboration +of open source contributors and the diversity of skills, experiences, +cultures, and opinions they bring to the projects and communities they +participate in. + +Every one of Google's open source projects and communities are inclusive +environments, based on treating all individuals respectfully, regardless +of gender identity and expression, sexual orientation, disabilities, +neurodiversity, physical appearance, body size, ethnicity, nationality, +race, age, religion, or similar personal characteristic. + +We value diverse opinions, but we value respectful behavior more. + +Respectful behavior includes: + +- Being considerate, kind, constructive, and helpful. +- Not engaging in demeaning, discriminatory, harassing, hateful, + sexualized, or physically threatening behavior, speech, and imagery. +- Not engaging in unwanted physical contact. + +Some Google open source projects +`may adopt <https://opensource.google/docs/releasing/preparing/#conduct>`__ +an explicit project code of conduct, which may have additional detailed +expectations for participants. Most of those projects will use our +`modified Contributor Covenant <https://opensource.google/docs/releasing/template/CODE_OF_CONDUCT/>`__. + +Resolve peacefully +~~~~~~~~~~~~~~~~~~ + +We do not believe that all conflict is necessarily bad; healthy debate +and disagreement often yields positive results. However, it is never +okay to be disrespectful. + +If you see someone behaving disrespectfully, you are encouraged to +address the behavior directly with those involved. Many issues can be +resolved quickly and easily, and this gives people more control over the +outcome of their dispute. If you are unable to resolve the matter for +any reason, or if the behavior is threatening or harassing, report it. +We are dedicated to providing an environment where participants feel +welcome and safe. + +Reporting problems +~~~~~~~~~~~~~~~~~~ + +Some Google open source projects may adopt a project-specific code of +conduct. In those cases, a Google employee will be identified as the +Project Steward, who will receive and handle reports of code of conduct +violations. In the event that a project hasn’t identified a Project +Steward, you can report problems by emailing opensource@google.com. + +We will investigate every complaint, but you may not receive a direct +response. We will use our discretion in determining when and how to +follow up on reported incidents, which may range from not taking action +to permanent expulsion from the project and project-sponsored spaces. We +will notify the accused of the report and provide them an opportunity to +discuss it before any action is taken. The identity of the reporter will +be omitted from the details of the report supplied to the accused. In +potentially harmful situations, such as ongoing harassment or threats to +anyone's safety, we may take action without notice. + +*This document was adapted from the* +`IndieWeb Code of Conduct <https://indieweb.org/code-of-conduct>`_ +*and can also be found at* <https://opensource.google/conduct/>. + +License +======= + +The SkyWater Open Source PDK GitHub actions are released under the +`Apache 2.0 license <https://github.com/google/skywater-pdk/blob/master/LICENSE>`_. + +The copyright details (which should also be found at the top of every file) are; + +:: + + Copyright 2021 SkyWater PDK Authors + + 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/run-drc-for-cell-gds-using-magic/README.src.rst b/run-drc-for-cell-gds-using-magic/README.src.rst new file mode 100644 index 0000000..50057f9 --- /dev/null +++ b/run-drc-for-cell-gds-using-magic/README.src.rst
@@ -0,0 +1,26 @@ +``skywater-pdk-actions`` - ``run-drc-for-cell-gds-using-magic`` +============================================================= + +This GitHub action runs Design Rule Checks on all GDS files inside the /cells +directory. + +Usage +===== + +Add this to any push, PR or manual dispatch workflow: + +.. code:: yml + + steps: + - uses: actions/checkout@v2 + + - name: Run Magic DRC + uses: docker://ghcr.io/google/skywater-pdk-actions-run-drc-for-cell-gds-using-magic:latest + with: + args: --acceptable-errors-file /dev/null --match-directories . --known-bad '' + +Check the Python file for more documentation on arguments. + +.. include:: ../docs/contributing.rst + +.. include:: ../docs/license.rst
diff --git a/run-drc-for-cell-gds-using-magic/requirements.txt b/run-drc-for-cell-gds-using-magic/requirements.txt new file mode 100644 index 0000000..b98f660 --- /dev/null +++ b/run-drc-for-cell-gds-using-magic/requirements.txt
@@ -0,0 +1 @@ +click \ No newline at end of file
diff --git a/run-drc-for-cell-gds-using-magic/run_all_drc.py b/run-drc-for-cell-gds-using-magic/run_all_drc.py new file mode 100644 index 0000000..5adcbcd --- /dev/null +++ b/run-drc-for-cell-gds-using-magic/run_all_drc.py
@@ -0,0 +1,231 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright 2020 SkyWater PDK Authors +# +# 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 +# +# https://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. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +run_all_drc.py --- A script that will run run_standard_drc for all .gds files +under the cells/ folder. + +Must be run from repository root. + +Usage: python3 run_all_drc.py --help + +Results: + + Prints a report to standard output. +""" + +import os +import re +import subprocess +import traceback + +from concurrent import futures +from typing import List, Tuple + +import click + +acceptable_errors = [] + +SCRIPT_DIR = os.path.realpath(os.path.dirname(__file__)) +STANDARD_DRC_SCRIPT = os.path.join(SCRIPT_DIR, "run_standard_drc.py") +PDK_SUBSET = os.getenv("PDK_ROOT") or os.path.join(SCRIPT_DIR, "sky130A") + +DRCError = Tuple[str, List[str]] + + +PARSE_DRC_REPORT_EXAMPLE = """ +This first set of lines is the 'header': +DRC errors for a cell that doesn't exist +It's skipped over by this function. +-------------------------------------------- + +This is an acceptable error. +These lines are details for the acceptable error. +There are usually a couple of lines. + +This is an unacceptable error. +These lines are details for the unacceptable error. +There are usually a couple of lines. + +This is another unacceptable error. +It has less lines of detail. +""" + + +def parse_drc_report( + report: str, acceptable_errors: List[str]) -> List[DRCError]: + """ + Takes a magic report in the format as seen in PARSE_DRC_REPORT_EXAMPLE + above, and returns all errors as a list of tuples, where the first element + of the tuple is the name of the error and the other lines are the details. + + >>> from pprint import pprint as p + >>> p(parse_drc_report( + ... PARSE_DRC_REPORT_EXAMPLE.strip(), + ... ["This is an acceptable error."])) + [('This is an unacceptable error.', + ['These lines are details for the unacceptable error.', + 'There are usually a couple of lines.']), + ('This is another unacceptable error.', ['It has less lines of detail.'])] + """ + components = [x.split("\n") for x in report.split("\n\n")] + errors = [] + + header = components.pop(0) # noqa: F841 + + for error in components: + error_name = error[0] + if error_name in acceptable_errors: + continue + errors.append((error[0], error[1:])) + + return errors + + +def drc_gds(path: str) -> Tuple[str, List[DRCError]]: + """ + Takes a GDS path. Returns the name of the cell and returns a list of + DRC errors. + """ + cell_name = os.path.basename(path)[:-4] + + env = os.environ.copy() + env["PDKPATH"] = PDK_SUBSET + + res = subprocess.run([ + "python3", + STANDARD_DRC_SCRIPT, + path + ], env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + report_path = path[:-4] + "_drc.txt" + try: + report = open(report_path).read() + + if os.getenv("ACTIONS_STEP_DEBUG") or False: + print("::group::%s" % report_path) + print(report) + print("::endgroup::") + + return cell_name, parse_drc_report(report, acceptable_errors) + except FileNotFoundError: + return cell_name, [ + ( + "Magic did not produce a report.", + [res.stdout.decode("utf8"), res.stderr.decode("utf8")] + ) + ] + + +@click.command() +@click.option( + "-a", + "--acceptable-errors-file", + default="/dev/null", + help="A file containing a list of newline-delimited acceptable DRC errors." + " Default: No file will be read and all errors deemed unacceptable." +) +@click.option( + "-m", + "--match-directories", + default=".", + help="A regex that will match subdirectories under cells/." + " Default: . (matches everything.)" +) +@click.option( + "-b", + "--known-bad", + default="", + help="A comma,delimited list of cells that are known bad and" + " thus do not cause a non-zero exit upon failure." + " Default: empty string (None of them.)" +) +def run_all_drc(acceptable_errors_file, match_directories, known_bad): + print("Testing cells in directories matching /%s/…" % match_directories) + + global acceptable_errors + acceptable_errors_str = open(acceptable_errors_file).read() + acceptable_errors = acceptable_errors_str.split("\n") + + known_bad_list = known_bad.split(",") + + nproc = os.cpu_count() + with futures.ThreadPoolExecutor(max_workers=nproc) as executor: + future_list = [] + + cells_dir = "./cells" + cells = os.listdir(cells_dir) + + for cell in cells: + if not re.match(match_directories, cell): + print("Skipping directory %s…" % cell) + continue + + cell_dir = os.path.join(cells_dir, cell) + + gds_list = list( + filter(lambda x: x.endswith(".gds"), os.listdir(cell_dir)) + ) + + for gds_name in gds_list: + gds_path = os.path.join(cell_dir, gds_name) + + future_list.append(executor.submit(drc_gds, gds_path)) + + successes = 0 + total = 0 + exit_code = 0 + for future in future_list: + total += 1 + cell_name, errors = future.result() + + symbol = "❌" + message = "ERROR" + if len(errors) == 0: + successes += 1 + # This tick is rendered black on all major platforms except for + # Microsoft. + symbol = "✔\ufe0f" + message = "CLEAN" + print("%-64s %s %s" % (cell_name, symbol, message)) + + if len(errors) != 0: + if cell_name not in known_bad_list: + exit_code = 65 + for error in errors: + print("* %s" % error[0]) + for line in error[1]: + print(" %s" % line) + + success_rate = (successes / total * 100) + print("%i/%i successes (%0.1f%%)" % (successes, total, success_rate)) + + exit(exit_code) + + +def main(): + try: + run_all_drc() + except Exception: + print("An unhandled exception has occurred.", traceback.format_exc()) + exit(69) + + +if __name__ == '__main__': + main()