-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
perform presubmission checks on custom GDS files submitted to Tiny Tapeout
- Loading branch information
Showing
12 changed files
with
1,298 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
name: Precheck Tests | ||
|
||
on: | ||
push: | ||
pull_request: | ||
workflow_dispatch: | ||
|
||
jobs: | ||
test-precheck: | ||
runs-on: ubuntu-latest | ||
env: | ||
PDK_ROOT: /home/runner/pdk | ||
PDK_VERSION: dd7771c384ed36b91a25e9f8b314355fc26561be | ||
|
||
steps: | ||
- name: Check out code | ||
uses: actions/checkout@v2 | ||
|
||
- name: Set up Python | ||
uses: actions/setup-python@v2 | ||
with: | ||
python-version: '3.11' | ||
cache: 'pip' | ||
|
||
- name: Install dependencies | ||
working-directory: precheck | ||
run: pip install -r requirements.txt | ||
|
||
- name: Install Sky130 PDK | ||
uses: TinyTapeout/volare-action@v1 | ||
with: | ||
pdk_name: sky130 | ||
pdk_version: ${{ env.PDK_VERSION }} | ||
pdk_root: ${{ env.PDK_ROOT }} | ||
|
||
- name: Install nix | ||
uses: cachix/install-nix-action@v20 | ||
|
||
- name: Run tests | ||
working-directory: precheck | ||
run: nix-shell --run "pytest" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
__pycache__ | ||
.pytest_cache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ pkgs ? import | ||
(builtins.fetchGit { | ||
url = "https://github.com/NixOS/nixpkgs.git"; | ||
rev = "f2bd8adf7b78d7616b52d0ef08865c7c2fcf189d"; | ||
}) | ||
{ } | ||
, magic ? import ./nix/magic.nix { inherit pkgs; } | ||
, | ||
}: | ||
|
||
pkgs.mkShell { | ||
buildInputs = [ | ||
pkgs.klayout | ||
magic | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import xml.etree.ElementTree as ET | ||
from typing import Dict | ||
|
||
|
||
class LayerInfo: | ||
def __init__(self, name: str, source: str, layer: int, data_type: int): | ||
self.name = name | ||
self.source = source | ||
self.layer = layer | ||
self.data_type = data_type | ||
|
||
def __repr__(self): | ||
return f"LayerInfo(name={self.name}, source={self.source}, layer={self.layer}, data_type={self.data_type})" | ||
|
||
|
||
def parse_lyp_layers(lyp_file: str): | ||
with open(lyp_file) as f: | ||
xml_content = f.read() | ||
root = ET.fromstring(xml_content) | ||
|
||
layers_dict: Dict[str, LayerInfo] = {} | ||
|
||
for properties in root.findall("properties"): | ||
name = properties.find("name") | ||
source = properties.find("source") | ||
valid = properties.find("valid") | ||
|
||
if valid is not None and valid.text == "false": | ||
continue | ||
|
||
if name is not None and source is not None: | ||
name_key = name.text.split("-")[0].strip() | ||
layer, data_type = source.text.split("@")[0].split("/") | ||
# Add the 'source' text as the value in the dictionary | ||
layers_dict[name_key] = LayerInfo( | ||
name=name.text, | ||
source=source.text, | ||
layer=int(layer), | ||
data_type=int(data_type), | ||
) | ||
|
||
return layers_dict |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# SPDX-FileCopyrightText: 2020 Efabless 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. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
|
||
set GDS_UT_PATH [lindex $argv 6] | ||
set DESIGN_NAME [lindex $argv 7] | ||
set PDK_PATH [lindex $argv 8] | ||
set DRC_REPORT [lindex $argv 9] | ||
set DRC_MAG [lindex $argv 10] | ||
|
||
gds read $GDS_UT_PATH | ||
|
||
set fout [open $DRC_REPORT w] | ||
set oscale [cif scale out] | ||
set cell_name $DESIGN_NAME | ||
magic::suspendall | ||
puts stdout "\[INFO\]: Loading $cell_name\n" | ||
flush stdout | ||
load $cell_name | ||
select top cell | ||
expand | ||
drc euclidean on | ||
drc style drc(full) | ||
drc check | ||
set drc_result [drc listall why] | ||
|
||
|
||
set count 0 | ||
puts $fout "$cell_name" | ||
puts $fout "----------------------------------------" | ||
foreach {errtype coordlist} $drc_result { | ||
puts $fout $errtype | ||
puts $fout "----------------------------------------" | ||
foreach coord $coordlist { | ||
set bllx [expr {$oscale * [lindex $coord 0]}] | ||
set blly [expr {$oscale * [lindex $coord 1]}] | ||
set burx [expr {$oscale * [lindex $coord 2]}] | ||
set bury [expr {$oscale * [lindex $coord 3]}] | ||
set coords [format " %.3f %.3f %.3f %.3f" $bllx $blly $burx $bury] | ||
puts $fout "$coords" | ||
set count [expr {$count + 1} ] | ||
} | ||
puts $fout "----------------------------------------" | ||
} | ||
|
||
puts $fout "\[INFO\]: COUNT: $count" | ||
puts $fout "\[INFO\]: Should be divided by 3 or 4" | ||
|
||
puts $fout "" | ||
close $fout | ||
|
||
puts stdout "\[INFO\]: DRC Checking DONE ($DRC_REPORT)" | ||
flush stdout | ||
|
||
if {$count > 0} { | ||
puts stderr "\[ERROR\]: $count DRC errors found" | ||
puts stdout "\[INFO\]: Saving mag view with DRC errors($DRC_MAG)" | ||
# WARNING: changes the name of the cell; keep as last step | ||
save $DRC_MAG | ||
puts stdout "\[INFO\]: Saved" | ||
flush stdout | ||
exit -1 | ||
} else { | ||
exit 0 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
# Copyright 2023 Efabless 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. | ||
|
||
# Copyright (c) 2003-2023 Eelco Dolstra and the Nixpkgs/NixOS contributors | ||
|
||
# Permission is hereby granted, free of charge, to any person obtaining | ||
# a copy of this software and associated documentation files (the | ||
# "Software"), to deal in the Software without restriction, including | ||
# without limitation the rights to use, copy, modify, merge, publish, | ||
# distribute, sublicense, and/or sell copies of the Software, and to | ||
# permit persons to whom the Software is furnished to do so, subject to | ||
# the following conditions: | ||
|
||
# The above copyright notice and this permission notice shall be | ||
# included in all copies or substantial portions of the Software. | ||
|
||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
{ pkgs ? import <nixpkgs> { } | ||
, | ||
}: | ||
|
||
with pkgs; clangStdenv.mkDerivation rec { | ||
name = "magic-vlsi"; | ||
rev = "11080465ad3759d4bdb3936026c797644c6f7f0b"; | ||
|
||
src = fetchFromGitHub { | ||
owner = "RTimothyEdwards"; | ||
repo = "magic"; | ||
inherit rev; | ||
sha256 = "sha256-zRIS8tFT52soPsySZdxVOX+5Pu8xriZeur0Gh8kdaXk="; | ||
}; | ||
|
||
nativeBuildInputs = [ python3 tcsh gnused ]; | ||
|
||
# So here's the situation: | ||
## * Magic will not compile without X11 libraries, even for headless use. | ||
## * Magic with Cairo will use Cairo-X11 headers that are not available on | ||
## pre-built Mac versions of Cairo which presume you're going to use Quartz. | ||
## -> Unintuitively, that means X11 libraries must be present on Mac, but not | ||
## Cairo. | ||
buildInputs = [ | ||
xorg.libX11 | ||
m4 | ||
mesa_glu | ||
ncurses | ||
tcl | ||
tcsh | ||
tk | ||
] ++ lib.optionals stdenv.isLinux [ cairo ]; | ||
|
||
configureFlags = [ | ||
"--with-tcl=${tcl}" | ||
"--with-tk=${tk}" | ||
"--disable-werror" | ||
]; | ||
|
||
preConfigure = '' | ||
# nix shebang fix | ||
patchShebangs ./scripts | ||
# "Precompute" git rev-parse HEAD | ||
sed -i 's@`git rev-parse HEAD`@${rev}@' ./scripts/defs.mak.in | ||
''; | ||
|
||
NIX_CFLAGS_COMPILE = "-Wno-implicit-function-declaration -Wno-parentheses -Wno-macro-redefined"; | ||
|
||
# Fairly sure this is deprecated? | ||
enableParallelBuilding = true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
#!/usr/bin/env python3 | ||
import argparse | ||
import logging | ||
import os | ||
import subprocess | ||
|
||
import klayout.db as pya | ||
import klayout.rdb as rdb | ||
from klayout_tools import parse_lyp_layers | ||
|
||
PDK_ROOT = os.getenv("PDK_ROOT") | ||
PDK_NAME = os.getenv("PDK_NAME") or "sky130A" | ||
LYP_FILE = f"{PDK_ROOT}/{PDK_NAME}/libs.tech/klayout/tech/{PDK_NAME}.lyp" | ||
REPORTS_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "reports") | ||
|
||
|
||
def magic_drc(gds: str, toplevel: str): | ||
logging.info(f"Running magic DRC on {gds} (module={toplevel})") | ||
|
||
magic = subprocess.run( | ||
[ | ||
"magic", | ||
"-noconsole", | ||
"-dnull", | ||
"-rcfile", | ||
f"{PDK_ROOT}/{PDK_NAME}/libs.tech/magic/{PDK_NAME}.magicrc", | ||
"magic_drc.tcl", | ||
gds, | ||
toplevel, | ||
PDK_ROOT, | ||
f"{REPORTS_PATH}/magic_drc.txt", | ||
f"{REPORTS_PATH}/magic_drc.mag", | ||
], | ||
) | ||
|
||
if magic.returncode != 0: | ||
logging.error("Magic DRC failed") | ||
return False | ||
|
||
return True | ||
|
||
|
||
def klayout_drc(gds: str, check: str): | ||
logging.info(f"Running klayout {check} on {gds}") | ||
report_file = f"{REPORTS_PATH}/drc_{check}.xml" | ||
klayout = subprocess.run( | ||
[ | ||
"klayout", | ||
"-b", | ||
"-r", | ||
f"tech-files/{PDK_NAME}_mr.drc", | ||
"-rd", | ||
f"{check}=true", | ||
"-rd", | ||
f"input={gds}", | ||
"-rd", | ||
f"report={report_file}", | ||
], | ||
) | ||
if klayout.returncode != 0: | ||
logging.error(f"Klayout {check} failed") | ||
return False | ||
|
||
report = rdb.ReportDatabase().load(report_file) | ||
|
||
if report.num_items() > 0: | ||
logging.error( | ||
f"Klayout {check} failed with {report.num_items()} DRC violations" | ||
) | ||
return False | ||
|
||
return True | ||
|
||
|
||
def klayout_checks(gds: str): | ||
layout = pya.Layout() | ||
layout.read(gds) | ||
layers = parse_lyp_layers(LYP_FILE) | ||
|
||
logging.info("Running forbidden layer check...") | ||
forbidden_layers = [ | ||
"met5.drawing", | ||
"met5.pin", | ||
"met5.label", | ||
] | ||
|
||
had_error = False | ||
for layer in forbidden_layers: | ||
layer_info = layers[layer] | ||
logging.info(f"* Checking {layer_info.name}") | ||
layer_index = layout.find_layer(layer_info.layer, layer_info.data_type) | ||
if layer_index is not None: | ||
logging.error(f"Forbidden layer {layer} found in {gds}") | ||
had_error = True | ||
|
||
if had_error: | ||
logging.error("Klayout checks failed") | ||
return not had_error | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("--gds", required=True) | ||
parser.add_argument("--toplevel", required=True) | ||
args = parser.parse_args() | ||
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") | ||
logging.info(f"PDK_ROOT: {PDK_ROOT}") | ||
|
||
assert magic_drc(args.gds, args.toplevel) | ||
|
||
assert klayout_drc(args.gds, "feol") | ||
assert klayout_drc(args.gds, "beol") | ||
assert klayout_drc(args.gds, "offgrid") | ||
|
||
assert klayout_checks(args.gds) | ||
|
||
logging.info(f"Precheck passed for {args.gds}! 🎉") | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
magic_drc.mag | ||
magic_drc.txt | ||
drc_feol.xml | ||
drc_beol.xml | ||
drc_offgrid.xml |
Oops, something went wrong.