diff --git a/.cockpitrc b/.cockpitrc index 1e5d987d..2cb6d9d6 100644 --- a/.cockpitrc +++ b/.cockpitrc @@ -64,11 +64,7 @@ libs = SG13CMOS5L_0.2 \ open_stdcell_rev0.1.3 \ - sg13cmos5l_stdcell_v0.1 \ sg13cmos5l_io_v0.1 \ - SG13G2_1.3.1 \ - ez130_8t_stdcell_v1.0 \ - open_io_v0.2 \ open_sram_v2.2 \ bondpad5l_70x70_v0 diff --git a/.github/scripts/run_full_flow.sh b/.github/scripts/run_full_flow.sh index 24dbe0aa..6eff31e3 100755 --- a/.github/scripts/run_full_flow.sh +++ b/.github/scripts/run_full_flow.sh @@ -9,7 +9,7 @@ # Full backend flow: # - Yosys synthesis # - OpenROAD P&R -# - KLayout GDS export +# - KLayout GDS export, sealing, and minimal DRC set -euo pipefail @@ -63,6 +63,13 @@ echo " Step 3: KLayout GDS export" echo "=============================================" cd klayout ./run_finishing.sh --gds --seal + +echo "" +echo "=============================================" +echo " Step 4: KLayout minimal DRC" +echo "=============================================" +./run_drc.sh --minimal --jobs 1 + cd "$CROC_ROOT" echo "" diff --git a/.github/workflows/full-flow.yml b/.github/workflows/full-flow.yml index cb7ee8ab..416decb9 100644 --- a/.github/workflows/full-flow.yml +++ b/.github/workflows/full-flow.yml @@ -33,6 +33,7 @@ jobs: with: cmd: ".github/scripts/run_full_flow.sh" - name: Upload openroad outputs + if: always() uses: actions/upload-artifact@v4 with: name: croc-openroad-out @@ -40,15 +41,25 @@ jobs: openroad/out openroad/logs openroad/reports - continue-on-error: true + if-no-files-found: ignore - name: Upload gds + if: always() uses: actions/upload-artifact@v4 with: name: croc-gds path: klayout/out/croc.gds.gz + if-no-files-found: ignore - name: Upload sealed gds + if: always() uses: actions/upload-artifact@v4 with: name: croc-gds-sealed path: klayout/out/croc.sealed.gds.gz - continue-on-error: true + if-no-files-found: ignore + - name: Upload DRC results + if: always() + uses: actions/upload-artifact@v4 + with: + name: croc-drc + path: klayout/drc-minimal + if-no-files-found: ignore diff --git a/README.md b/README.md index 007e5d6d..5b0cb6e4 100644 --- a/README.md +++ b/README.md @@ -184,8 +184,8 @@ Maintainers can update the pinned PDK mirror with `scripts/update_pdk.sh`. You can also run `scripts/setup_technology.sh` manually to print the active technology view. `env.sh` runs the same setup quietly before the ASIC scripts use the PDK. KLayout needs technology scripts, filling rules, and layer properties in addition to the files mentioned above. -On ETH systems, the installed CMOS5L KLayout support package takes priority when available. -Otherwise, the appropriate submodule, either `ihp13/sg13cmos5l` or `ihp13/sg13g2`, is required for the KLayout setup. +KLayout tool files are always taken from the pinned PDK submodule, either +`ihp13/sg13cmos5l` or `ihp13/sg13g2`. The OSIC/oseda container also contains a PDK under `/foss/pdks`, but Croc does not currently use it. @@ -207,9 +207,20 @@ To run the synthesis and place & route flow execute: git submodule update --init --recursive cd yosys && ./run_synthesis.sh --synth cd ../openroad && ./run_backend.sh --all -cd ../klayout && ./run_finishing.sh --gds +cd ../klayout && ./run_finishing.sh --gds --seal +./run_drc.sh --minimal ``` +Use `./run_drc.sh --minimal` after OpenROAD and during layout iteration. It +checks the interconnect and top-level layout produced by the implementation +flow while skipping transistor layers inside library cells. This keeps it +quick enough for regular CI and layout iteration. It is not a tapeout signoff +check. + +Use `./run_drc.sh --full` when reviewing the final layout before tapeout. It +runs the slower and stricter checks across all layers. Full DRC is the default +when no mode is specified. + The ASIC flow defaults to `CROC_PDK=sg13cmos5l`. To switch back to the original metal stack, export `CROC_PDK=sg13g2` before running the scripts. diff --git a/env.sh b/env.sh index 9da540e1..4f29f5d4 100644 --- a/env.sh +++ b/env.sh @@ -11,11 +11,7 @@ # This file is sourced by all scripts to set up tool paths and PDK location # Determine repository root -if [[ -n "${BASH_SOURCE[0]}" ]]; then - export CROC_ROOT=$(realpath $(dirname "${BASH_SOURCE[0]}")) -else - export CROC_ROOT=$(pwd) -fi +export CROC_ROOT="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" echo "[INFO][ENV] Croc root: $CROC_ROOT" @@ -24,7 +20,6 @@ echo "[INFO][ENV] Croc root: $CROC_ROOT" ###################### export PROJ_NAME="${PROJ_NAME:-croc}" export TOP_DESIGN="${TOP_DESIGN:-croc_chip}" -export DUT_DESIGN="${DUT_DESIGN:-croc_soc}" if [[ -z "${CROC_PDK:-}" ]]; then if [[ "${PDK:-}" == "sg13g2" || "${PDK:-}" == "sg13cmos5l" ]]; then echo "[WARNING][ENV] Ignoring PDK=$PDK. Use CROC_PDK to select the Croc PDK." @@ -38,22 +33,36 @@ export PDK_STDCELL_LEF_FILE="sg13g2_stdcell.lef" export PDK_STDCELL_GDS_FILE="sg13g2_stdcell.gds" export PDK_STDCELL_LIB_TT_FILE="sg13g2_stdcell_typ_1p20V_25C.lib" export PDK_STDCELL_LIB_FF_FILE="sg13g2_stdcell_fast_1p32V_m40C.lib" -export PDK_TECH_LEF_FILE="sg13cmos5l_tech.lef" -export PDK_IO_LEF_FILE="sg13cmos5l_io.lef" -export PDK_IO_GDS_FILE="sg13cmos5l_io.gds" -export PDK_IO_LIB_TT_FILE="sg13cmos5l_io_typ_1p2V_3p3V_25C.lib" -export PDK_IO_LIB_FF_FILE="sg13cmos5l_io_fast_1p32V_3p6V_m40C.lib" -export PDK_LAYER_MAP_FILE="" export CROC_TECHNOLOGY_VIEW="" -export PDK_BONDPAD_CELL="bondpad_70x70" -export PDK_BONDPAD_LEF="bondpad_70x70.lef" -export PDK_BONDPAD_GDS="bondpad_70x70.gds" -if [[ "$CROC_PDK" == "sg13cmos5l" ]]; then - export BENDER_PDK_ARGS="-D IHP_SG13CMOS5L=1" -else - export BENDER_PDK_ARGS="" -fi +case "$CROC_PDK" in + sg13cmos5l) + export BENDER_PDK_ARGS="-D IHP_SG13CMOS5L=1" + export PDK_TECH_LEF_FILE="sg13cmos5l_tech.lef" + export PDK_IO_LEF_FILE="sg13cmos5l_io.lef" + export PDK_IO_GDS_FILE="sg13cmos5l_io.gds" + export PDK_IO_LIB_TT_FILE="sg13cmos5l_io_typ_1p2V_3p3V_25C.lib" + export PDK_IO_LIB_FF_FILE="sg13cmos5l_io_fast_1p32V_3p6V_m40C.lib" + export PDK_BONDPAD_LEF="bondpad5l_70x70.lef" + export PDK_BONDPAD_GDS="bondpad5l_70x70.gds" + export KLAYOUT_PATH="$CROC_ROOT/ihp13/sg13cmos5l/libs.tech/klayout" + ;; + sg13g2) + export BENDER_PDK_ARGS="" + export PDK_TECH_LEF_FILE="sg13g2_tech.lef" + export PDK_IO_LEF_FILE="sg13g2_io.lef" + export PDK_IO_GDS_FILE="sg13g2_io.gds" + export PDK_IO_LIB_TT_FILE="sg13g2_io_typ_1p2V_3p3V_25C.lib" + export PDK_IO_LIB_FF_FILE="sg13g2_io_fast_1p32V_3p6V_m40C.lib" + export PDK_BONDPAD_LEF="bondpad_70x70.lef" + export PDK_BONDPAD_GDS="bondpad_70x70.gds" + export KLAYOUT_PATH="$CROC_ROOT/ihp13/sg13g2/ihp-sg13g2/libs.tech/klayout" + ;; + *) + echo "[ERROR][ENV] Unknown CROC_PDK '$CROC_PDK'" >&2 + return 1 2>/dev/null || exit 1 + ;; +esac ################### @@ -80,11 +89,6 @@ if [[ -e "${CROC_ROOT}/technology" || ( "${CROC_SKIP_TECH_SETUP:-0}" == "1" && - # Dry runs use the public mirror directly without creating technology/. export PDK_ROOT="$PUBLIC_PDK_ROOT" fi - export PDK_TECH_HOME="$PDK_ROOT" - export PDK_STD_HOME="$PDK_ROOT" - export PDK_SRAM_HOME="$PDK_ROOT" - export PDK_IO_HOME="$PDK_ROOT" - public_pdk_realpath="$(realpath -m "$PUBLIC_PDK_ROOT")" technology_realpath="$(realpath -m "$PDK_ROOT")" if [[ "$technology_realpath" == "$public_pdk_realpath" ]]; then @@ -106,36 +110,6 @@ if [[ -e "${CROC_ROOT}/technology" || ( "${CROC_SKIP_TECH_SETUP:-0}" == "1" && - export PDK_DIR_GDS_IOS="$PDK_ROOT/gds" export PDK_DIR_GDS_BOND="$PDK_ROOT/gds" - if [[ "$CROC_PDK" == "sg13cmos5l" ]]; then - export PDK_BONDPAD_CELL="bondpad5l_70x70" - export PDK_BONDPAD_GDS="bondpad5l_70x70.gds" - export PDK_TECH_LEF_FILE="sg13cmos5l_tech.lef" - export PDK_IO_LEF_FILE="sg13cmos5l_io.lef" - export PDK_IO_GDS_FILE="sg13cmos5l_io.gds" - export PDK_IO_LIB_TT_FILE="sg13cmos5l_io_typ_1p2V_3p3V_25C.lib" - export PDK_IO_LIB_FF_FILE="sg13cmos5l_io_fast_1p32V_3p6V_m40C.lib" - export PDK_BONDPAD_LEF="bondpad5l_70x70.lef" - - # KLayout needs separate tool files. Cockpit takes precedence. - export KLAYOUT_PATH="$CROC_ROOT/ihp13/sg13cmos5l/libs.tech/klayout" - if [[ -d "/usr/pack/ihp-sg13-kgf/open_ihp_sg13cmos5l/sg13cmos5l_tech/v0.2/klayout" ]]; then - export KLAYOUT_PATH="/usr/pack/ihp-sg13-kgf/open_ihp_sg13cmos5l/sg13cmos5l_tech/v0.2/klayout" - fi - elif [[ "$CROC_PDK" == "sg13g2" ]]; then - export PDK_TECH_LEF_FILE="sg13g2_tech.lef" - export PDK_IO_LEF_FILE="sg13g2_io.lef" - export PDK_IO_GDS_FILE="sg13g2_io.gds" - export PDK_IO_LIB_TT_FILE="sg13g2_io_typ_1p2V_3p3V_25C.lib" - export PDK_IO_LIB_FF_FILE="sg13g2_io_fast_1p32V_3p6V_m40C.lib" - export PDK_BONDPAD_CELL="bondpad_70x70" - export PDK_BONDPAD_LEF="bondpad_70x70.lef" - export PDK_BONDPAD_GDS="bondpad_70x70.gds" - export KLAYOUT_PATH="$CROC_ROOT/ihp13/sg13g2/ihp-sg13g2/libs.tech/klayout" - else - echo "[ERROR][ENV] Unknown CROC_PDK '$CROC_PDK'" - exit 1 - fi - else echo "[WARNING][ENV] PDK not found. Initialize cockpit or ensure ihp13/pdk exists" export PDK_ROOT="" # Set to empty to avoid unbound variable error @@ -150,10 +124,6 @@ echo "[INFO][ENV] Bender PDK args: ${BENDER_PDK_ARGS:-}" echo "[INFO][ENV] Tech LEF: $PDK_TECH_LEF_FILE" echo "[INFO][ENV] IO LEF/GDS: $PDK_IO_LEF_FILE/$PDK_IO_GDS_FILE" echo "[INFO][ENV] IO Liberty tt/ff: $PDK_IO_LIB_TT_FILE/$PDK_IO_LIB_FF_FILE" -echo "[INFO][ENV] Bondpad cell: $PDK_BONDPAD_CELL ($PDK_BONDPAD_LEF/$PDK_BONDPAD_GDS)" +echo "[INFO][ENV] Bondpad LEF/GDS: $PDK_BONDPAD_LEF/$PDK_BONDPAD_GDS" export KLAYOUT_TECH="$CROC_PDK" -if [[ -f "$PDK_ROOT/SG13G2_streamout.map" ]]; then - export PDK_LAYER_MAP_FILE="$PDK_ROOT/SG13G2_streamout.map" -else - export PDK_LAYER_MAP_FILE="$KLAYOUT_PATH/tech/${CROC_PDK}.map" -fi +export PDK_LAYER_MAP_FILE="$KLAYOUT_PATH/tech/${CROC_PDK}.map" diff --git a/ihp13/sg13cmos5l b/ihp13/sg13cmos5l index 5958eb38..43ac6216 160000 --- a/ihp13/sg13cmos5l +++ b/ihp13/sg13cmos5l @@ -1 +1 @@ -Subproject commit 5958eb38973d5c56e7a26f65fc23d9f3a740ccd2 +Subproject commit 43ac6216df29c043f97d462706f98a91fa70e3ea diff --git a/ihp13/sg13g2 b/ihp13/sg13g2 index ee974c3a..c1d6f49e 160000 --- a/ihp13/sg13g2 +++ b/ihp13/sg13g2 @@ -1 +1 @@ -Subproject commit ee974c3adc69d0f36adbf20577079f0df419d702 +Subproject commit c1d6f49ea902a88e328dda022805da8887ea26e3 diff --git a/klayout/.gitignore b/klayout/.gitignore index c47e9b4d..0eb451fc 100644 --- a/klayout/.gitignore +++ b/klayout/.gitignore @@ -1,2 +1,4 @@ .klayout out +__pycache__/ +drc-*/ diff --git a/klayout/run_drc.sh b/klayout/run_drc.sh new file mode 100755 index 00000000..50d030ab --- /dev/null +++ b/klayout/run_drc.sh @@ -0,0 +1,133 @@ +#!/usr/bin/env bash +# Copyright (c) 2026 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CROC_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +DRYRUN=0 +MODE="full" +MODE_SELECTED=0 +JOBS="${DRC_JOBS:-1}" + +show_help() { + cat <<'EOF' +KLayout DRC coordinator + +Usage: ./run_drc.sh [OPTIONS] + +Options: + --minimal Check the sealed layout during implementation + --full Check the sealed layout before tapeout (default) + --jobs N Run at most N DRC tables concurrently (default: 1) + --dry-run, -n Print the planned command without changing files + --verbose, -v Print commands while executing + --help, -h Show this help message + +Use --minimal after OpenROAD and during floorplan or routing iteration. It +checks the generated interconnect and top-level layout while skipping the +transistor layers inside library cells, making it suitable for regular CI. +It is not a tapeout signoff check. Use --full for the final pre-tapeout review; +it runs slower, stricter checks across all layers. + +Both modes use klayout/out/.sealed.gds.gz and top cell +_sealed. Results are written to klayout/drc-minimal or +klayout/drc-full. +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --help|-h) show_help; exit 0 ;; + --dry-run|-n) DRYRUN=1; shift ;; + --verbose|-v) set -x; shift ;; + --minimal|--full) + if [[ "$MODE_SELECTED" -eq 1 ]]; then + echo "[ERROR][KLayout] Select only one DRC mode" >&2 + exit 2 + fi + MODE="${1#--}" + MODE_SELECTED=1 + shift + ;; + --jobs) + [[ $# -ge 2 ]] || { echo "[ERROR][KLayout] --jobs requires a value" >&2; exit 2; } + JOBS="$2" + shift 2 + ;; + *) + echo "[ERROR][KLayout] Unknown option: $1 (use --help for usage)" >&2 + exit 2 + ;; + esac +done + +if [[ ! "$JOBS" =~ ^[1-9][0-9]*$ ]]; then + echo "[ERROR][KLayout] --jobs must be a positive integer" >&2 + exit 2 +fi +if [[ "$DRYRUN" -eq 1 ]]; then + export CROC_SKIP_TECH_SETUP=1 +fi +source "$CROC_ROOT/env.sh" + +GDS_PATH="$SCRIPT_DIR/out/${PROJ_NAME}.sealed.gds.gz" +TOP_CELL="${TOP_DESIGN}_sealed" +RUN_DIR="$SCRIPT_DIR/drc-$MODE" +DRC_RUNNER="$KLAYOUT_PATH/tech/drc/run_drc.py" + +# The upstream runner builds a shell command from these values. +for value in "$GDS_PATH" "$TOP_CELL" "$RUN_DIR"; do + if [[ ! "$value" =~ ^[A-Za-z0-9_./:@%+,=-]+$ ]]; then + echo "[ERROR][KLayout] DRC path contains unsupported characters: $value" >&2 + exit 1 + fi +done + +if [[ "$DRYRUN" -eq 0 ]]; then + [[ -f "$DRC_RUNNER" ]] || { + echo "[ERROR][KLayout] DRC runner not found: $DRC_RUNNER" >&2 + exit 1 + } + [[ -f "$GDS_PATH" ]] || { + echo "[ERROR][KLayout] Input GDS not found: $GDS_PATH" >&2 + exit 1 + } + mkdir -p "$RUN_DIR" +fi + +command=( + python3 "$DRC_RUNNER" + "--path=$GDS_PATH" + "--topcell=$TOP_CELL" + "--run_dir=$RUN_DIR" + "--mp=$JOBS" + --density_thr=1 +) +if [[ "$MODE" == "minimal" ]]; then + command+=(--precheck_drc --no_feol --no_recommended --no_offgrid --no_density --disable_extra_rules) + echo "[INFO][KLayout] Running minimal DRC on $TOP_CELL with $JOBS worker(s)" +else + command+=(--antenna) + echo "[INFO][KLayout] Running full DRC on $TOP_CELL with $JOBS worker(s)" +fi + +if [[ "$DRYRUN" -eq 1 ]]; then + printf '%q ' "${command[@]}" + printf '\n' + exit 0 +fi + +coordinator_log="$RUN_DIR/drc-coordinator.log" +set +e +"${command[@]}" 2>&1 | tee "$coordinator_log" +run_status=${PIPESTATUS[0]} +set -e +if [[ "$run_status" -ne 0 ]]; then + exit "$run_status" +fi +if grep -Fq " generated an exception:" "$coordinator_log"; then + echo "[ERROR][KLayout] One or more parallel DRC checks failed; see $coordinator_log" >&2 + exit 1 +fi