From 147548ecd6e39c016f9b0fb205dffa664c6283d3 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 8 Jan 2026 12:03:03 +0100 Subject: [PATCH 1/2] finishing: update PDK to newest version, adapt flow to std cells r0.1.3 --- ihp13/pdk | 2 +- openroad/scripts/chip.tcl | 2 +- openroad/scripts/floorplan.tcl | 7 +++-- openroad/scripts/init_tech.tcl | 14 ++++----- openroad/scripts/power_grid.tcl | 53 +++++++++++++++++---------------- 5 files changed, 40 insertions(+), 38 deletions(-) diff --git a/ihp13/pdk b/ihp13/pdk index 035037f6..a2bf8ea8 160000 --- a/ihp13/pdk +++ b/ihp13/pdk @@ -1 +1 @@ -Subproject commit 035037f6312851414e4bb5daa5a9d18975c768b3 +Subproject commit a2bf8ea81aee7d0fcdd6d62168edca0d7d0bcb08 diff --git a/openroad/scripts/chip.tcl b/openroad/scripts/chip.tcl index f3f3ec83..0961be05 100644 --- a/openroad/scripts/chip.tcl +++ b/openroad/scripts/chip.tcl @@ -182,7 +182,7 @@ utl::report "Repair clock inverters" repair_clock_inverters utl::report "Clock Tree Synthesis" -set_wire_rc -clock -layer Metal4 +set_wire_rc -clock -layer Metal3 clock_tree_synthesis -buf_list $ctsBuf -root_buf $ctsBufRoot \ -sink_clustering_enable \ -obstruction_aware \ diff --git a/openroad/scripts/floorplan.tcl b/openroad/scripts/floorplan.tcl index c99955af..96035df9 100644 --- a/openroad/scripts/floorplan.tcl +++ b/openroad/scripts/floorplan.tcl @@ -88,6 +88,7 @@ set core_topY [lindex $coreArea 3] # We need to define the metal tracks # (where the wires on each metal should go) # this function is defined in init_tech.tcl +utl::report "Create Tracks" makeTracks # the height of a standard cell, useful to align things @@ -104,8 +105,8 @@ source src/instances.tcl # Placing ########################################################################## # use these for macro placement -set floorPaddingX 20.0 -set floorPaddingY 20.0 +set floorPaddingX 16.0 +set floorPaddingY 16.0 set floor_leftX [expr $core_leftX + $floorPaddingX] set floor_bottomY [expr $core_bottomY + $floorPaddingY] set floor_rightX [expr $core_rightX - $floorPaddingX] @@ -126,4 +127,4 @@ set Y [expr $Y - $RamSize256x64_H - 15] placeInstance $bank1_sram0 $X $Y R0 -cut_rows -halo_width_x 2 -halo_width_y 1 \ No newline at end of file +cut_rows -halo_width_x 5 -halo_width_y 2 diff --git a/openroad/scripts/init_tech.tcl b/openroad/scripts/init_tech.tcl index 625e4f8c..b76170f4 100644 --- a/openroad/scripts/init_tech.tcl +++ b/openroad/scripts/init_tech.tcl @@ -62,7 +62,7 @@ read_lef ${pdk_cells_lef}/sg13g2_stdcell.lef read_lef ${pdk_io_lef}/sg13g2_io.lef read_lef ${pdk_pad_lef}/bondpad_70x70.lef -foreach file [glob -directory $pdk_sram_lef *.lef] { +foreach file [glob -directory $pdk_sram_lef RM_IHPSG13*.lef] { read_lef "$file" } @@ -79,11 +79,11 @@ set dont_use_cells sg13g2_IOPad* proc makeTracks {} { utl::report "Metal Tracks" - make_tracks Metal1 -x_offset 0 -x_pitch 0.48 -y_offset 0 -y_pitch 0.48 - make_tracks Metal2 -x_offset 0 -x_pitch 0.42 -y_offset 0 -y_pitch 0.42 - make_tracks Metal3 -x_offset 0 -x_pitch 0.48 -y_offset 0 -y_pitch 0.48 - make_tracks Metal4 -x_offset 0 -x_pitch 0.42 -y_offset 0 -y_pitch 0.42 - make_tracks Metal5 -x_offset 0 -x_pitch 0.48 -y_offset 0 -y_pitch 0.48 - make_tracks TopMetal1 -x_offset 1.46 -x_pitch 2.28 -y_offset 1.46 -y_pitch 2.28 + make_tracks Metal1 -x_offset 0 -x_pitch 0.42 -y_offset 0 -y_pitch 0.42 + make_tracks Metal2 -x_offset 0 -x_pitch 0.48 -y_offset 0 -y_pitch 0.48 + make_tracks Metal3 -x_offset 0 -x_pitch 0.42 -y_offset 0 -y_pitch 0.42 + make_tracks Metal4 -x_offset 0 -x_pitch 0.48 -y_offset 0 -y_pitch 0.48 + make_tracks Metal5 -x_offset 0 -x_pitch 0.42 -y_offset 0 -y_pitch 0.42 + make_tracks TopMetal1 -x_offset 1.64 -x_pitch 2.28 -y_offset 1.64 -y_pitch 2.28 make_tracks TopMetal2 -x_offset 2.00 -x_pitch 4.00 -y_offset 2.00 -y_pitch 4.00 } diff --git a/openroad/scripts/power_grid.tcl b/openroad/scripts/power_grid.tcl index 80cb688f..62abc81b 100644 --- a/openroad/scripts/power_grid.tcl +++ b/openroad/scripts/power_grid.tcl @@ -29,18 +29,20 @@ if {[info exists power_grid_defined]} { ## Power settings ########################################################################## # Core Power Ring -## Offset from core to power ring -set pgcrOffset 2 +## Space between pads and core -> used for power ring +set PowRingSpace 35 ## Spacing must meet TM2 rules -set pgcrSpacing 6 +set pgcrSpacing 4 ## Width must meet TM2 rules -set pgcrWidth 10 +set pgcrWidth 8 +## Offset from core to power ring +set pgcrOffset [expr ($PowRingSpace - $pgcrSpacing - 2 * $pgcrWidth) / 2] -# TopMetal2 Core Power Grid -set tpg2Width 6; # arbitrary number -set tpg2Pitch 204; # multiple of pad-pitch -set tpg2Spacing 60; # big enough to skip over a pad -set tpg2Offset 97; # offset from leftX of core +# TopMetal1 Core Power Grid +set tpg1Width 3; # arbitrary number +set tpg1Pitch 228; # multiple of pad-pitch +set tpg1Spacing 60; # big enough to skip over a pad +set tpg1Offset 97; # offset from leftX of core # Macro Power Rings -> M3 and M2 ## Spacing must be larger than pitch of M2/M3 @@ -52,7 +54,7 @@ set mprOffsetX 2.4 set mprOffsetY 0.6 # macro power grid (stripes on TopMetal1/TopMetal2 depending on orientation) -set mpgWidth 6 +set mpgWidth 3 set mpgSpacing 4 set mpgOffset 20; # arbitrary @@ -83,18 +85,18 @@ proc sram_power { name macro } { set stripe_dist [expr $stripe_dist/2] } - add_pdn_stripe -grid ${name}_grid -layer {TopMetal1} -width $mpgWidth -spacing $mpgSpacing \ + add_pdn_stripe -grid ${name}_grid -layer {Metal5} -width $mpgWidth -spacing $mpgSpacing \ -pitch $stripe_dist -offset $mpgOffset -extend_to_core_ring -starts_with POWER -snap_to_grid # Connection of Macro Power Ring to standard-cell rails - add_pdn_connect -grid ${name}_grid -layers {Metal3 Metal1} + add_pdn_connect -grid ${name}_grid -layers {Metal4 Metal2} + add_pdn_connect -grid ${name}_grid -layers {Metal4 Metal1} # Connection of Stripes on Macro to Macro Power Ring - add_pdn_connect -grid ${name}_grid -layers {TopMetal1 Metal3} - add_pdn_connect -grid ${name}_grid -layers {TopMetal1 Metal4} + add_pdn_connect -grid ${name}_grid -layers {Metal5 Metal4} # Connection of Stripes on Macro to Macro Power Pins # add_pdn_connect -grid ${name}_grid -layers {TopMetal1 Metal4} # Connection of Stripes on Macro to Core Power Stripes - add_pdn_connect -grid ${name}_grid -layers {TopMetal2 TopMetal1} + add_pdn_connect -grid ${name}_grid -layers {TopMetal1 Metal5} } @@ -112,28 +114,27 @@ add_pdn_ring -grid {core_grid} \ -connect_to_pad_layers TopMetal2 # M1 Standardcell Rows (tracks) -add_pdn_stripe -grid {core_grid} -layer {Metal1} -width {0.44} -offset {0} \ +add_pdn_stripe -grid {core_grid} -layer {Metal1} -width {0.32} -offset {0} \ -followpins -extend_to_core_ring sram_power "sram_256x64" "RM_IHPSG13_1P_256x64_c2_bm_bist" # Top power grid -# Top 2 Stripe -add_pdn_stripe -grid {core_grid} -layer {TopMetal2} -width $tpg2Width \ - -pitch $tpg2Pitch -spacing $tpg2Spacing -offset $tpg2Offset \ - -extend_to_core_ring -snap_to_grid -number_of_straps 7 +# Top 1 Stripe +add_pdn_stripe -grid {core_grid} -layer {TopMetal1} -width $tpg1Width \ + -pitch $tpg1Pitch -spacing $tpg1Spacing -offset $tpg1Offset \ + -extend_to_core_ring -snap_to_grid -number_of_straps 7 # "The add_pdn_connect command is used to define which layers in the power grid are to be connected together. # During power grid generation, vias will be added for overlapping power nets and overlapping ground nets." -# M1 is declared vertical but tracks still horizontal -# vertical TopMetal2 to below horizonals (M1 has horizontal power tracks) -add_pdn_connect -grid {core_grid} -layers {TopMetal2 Metal1} -add_pdn_connect -grid {core_grid} -layers {TopMetal2 Metal2} -add_pdn_connect -grid {core_grid} -layers {TopMetal2 Metal4} -# add_pdn_connect -grid {core_grid} -layers {TopMetal2 TopMetal1} +# vertical TopMetal1 to below horizonals (M1 has horizontal power tracks) +add_pdn_connect -grid {core_grid} -layers {TopMetal1 Metal1} +add_pdn_connect -grid {core_grid} -layers {TopMetal1 Metal3} +add_pdn_connect -grid {core_grid} -layers {TopMetal1 Metal5} # power ring to standard cell rails add_pdn_connect -grid {core_grid} -layers {Metal3 Metal2} +add_pdn_connect -grid {core_grid} -layers {Metal2 Metal1} ########################################################################## From 92134f02ece91b3750ac9100466053076fe573f5 Mon Sep 17 00:00:00 2001 From: Thomas Benz Date: Thu, 8 Jan 2026 12:07:54 +0100 Subject: [PATCH 2/2] finishing: patch PDK, cleanup def2stream, include seal and metal filling steps --- .github/workflows/artistic.yml | 4 +- .github/workflows/full-flow.yml | 11 +- Makefile | 20 +- ihp13/.gitignore | 1 + ihp13/patches/0001-Filling-improvements.patch | 426 ++++++++++++++++++ klayout/.gitignore | 2 +- klayout/def2gds.sh | 89 ---- klayout/{ => scripts}/def2stream.py | 60 +-- klayout/scripts/finishing.sh | 182 ++++++++ klayout/scripts/merge_sealring.py | 38 ++ klayout/sg13g2.map | 154 ------- 11 files changed, 697 insertions(+), 290 deletions(-) create mode 100644 ihp13/.gitignore create mode 100644 ihp13/patches/0001-Filling-improvements.patch delete mode 100755 klayout/def2gds.sh rename klayout/{ => scripts}/def2stream.py (53%) create mode 100644 klayout/scripts/finishing.sh create mode 100644 klayout/scripts/merge_sealring.py delete mode 100644 klayout/sg13g2.map diff --git a/.github/workflows/artistic.yml b/.github/workflows/artistic.yml index f1b4fab3..52dbef16 100644 --- a/.github/workflows/artistic.yml +++ b/.github/workflows/artistic.yml @@ -49,7 +49,7 @@ jobs: github-token: ${{ github.token }} repository: ${{ github.repository }} run-id: ${{ steps.get-run-id.outputs.run-id }} - path: klayout + path: klayout/out - name: Download DEF artifact from "Full Flow" workflow uses: actions/download-artifact@v4 with: @@ -93,7 +93,7 @@ jobs: - name: Meercat setup, export top-level GDS uses: ./.github/actions/oseda-cmd with: - cmd: "ls -lah klayout; cd artistic; mkdir -p meerkat_work; python3 scripts/meerkat_interface.py -i ../../klayout/croc_chip.gds -m croc_tm.gds.gz -g croc_logo.gds -o croc_chip.gds.gz -w meerkat_work -l 134; cd meerkat_work; klayout -zz -rm ../scripts/export_top_metal.py; gzip -d croc_tm.gds.gz" + cmd: "ls -lah klayout; cd artistic; mkdir -p meerkat_work; python3 scripts/meerkat_interface.py -i ../../klayout/out/croc_chip.gds.gz -m croc_tm.gds.gz -g croc_logo.gds -o croc_chip.gds.gz -w meerkat_work -l 134; cd meerkat_work; klayout -zz -rm ../scripts/export_top_metal.py; gzip -d croc_tm.gds.gz" - name: Upload top-level GDS uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/full-flow.yml b/.github/workflows/full-flow.yml index ed315337..7cf3093d 100644 --- a/.github/workflows/full-flow.yml +++ b/.github/workflows/full-flow.yml @@ -25,13 +25,13 @@ jobs: - name: Checkout repository (with submodules) uses: actions/checkout@v4 with: - submodules: true + submodules: recursive - name: Free disk space uses: ./.github/actions/free-space - name: Run Yosys, OpenROAD and KLayout uses: ./.github/actions/oseda-cmd with: - cmd: "make yosys && make openroad && make klayout" + cmd: "make yosys && make openroad && make finishing" - name: Upload openroad outputs uses: actions/upload-artifact@v4 with: @@ -44,5 +44,10 @@ jobs: uses: actions/upload-artifact@v4 with: name: croc-gds - path: klayout/croc_chip.gds + path: klayout/out/croc_chip.gds.gz + - name: Upload sealed gds + uses: actions/upload-artifact@v4 + with: + name: croc-gds-sealed + path: klayout/out/croc_chip_sealed.gds.gz continue-on-error: true diff --git a/Makefile b/Makefile index af084f8f..f1b526bb 100644 --- a/Makefile +++ b/Makefile @@ -126,13 +126,21 @@ yosys-flist: Bender.lock Bender.yml rtl/*/Bender.yml include yosys/yosys.mk include openroad/openroad.mk -klayout/croc_chip.gds: $(OR_OUT)/croc.def klayout/*.sh klayout/*.py - ./klayout/def2gds.sh +.PHONY: yosys-flist -## Generate merged .gds from openroads .def output -klayout: klayout/croc_chip.gds -.PHONY: klayout yosys-flist +############# +# Finishing # +############# +ihp13/pdk.patched: + - cd ihp13/pdk; git apply ../patches/0001-Filling-improvements.patch + touch $@ + +klayout/out/croc_chip_sealed.gds.gz: ihp13/pdk.patched openroad/out/croc.def klayout/scripts/*.py klayout/scripts/*.sh + cd klayout; bash scripts/finishing.sh + +finishing: klayout/out/croc_chip_sealed.gds.gz +.PHONY: finishing ################# @@ -176,7 +184,7 @@ format: ## Delete generated files and directories clean: rm -f $(SV_FLIST) - rm -f klayout/croc_chip.gds + rm -rf klayout/out/ rm -rf verilator/obj_dir/ rm -f verilator/croc.f rm -f verilator/croc.vcd diff --git a/ihp13/.gitignore b/ihp13/.gitignore new file mode 100644 index 00000000..9beb6e1a --- /dev/null +++ b/ihp13/.gitignore @@ -0,0 +1 @@ +*.patched diff --git a/ihp13/patches/0001-Filling-improvements.patch b/ihp13/patches/0001-Filling-improvements.patch new file mode 100644 index 00000000..cc3340b4 --- /dev/null +++ b/ihp13/patches/0001-Filling-improvements.patch @@ -0,0 +1,426 @@ +From 6603c4b764475516639f7843b6ac370616800956 Mon Sep 17 00:00:00 2001 +From: Thomas Benz +Date: Thu, 4 Sep 2025 16:39:22 +0200 +Subject: [PATCH] Filling improvements + +--- + .../tech/macros/sg13g2_filler_ActGatP.lym | 6 + + .../tech/macros/sg13g2_filler_Metal.lym | 35 +++-- + .../tech/macros/sg13g2_filler_TopMetal1.lym | 132 ++++++++++++++++++ + .../tech/macros/sg13g2_filler_TopMetal2.lym | 131 +++++++++++++++++ + .../libs.tech/klayout/tech/scripts/filler.py | 8 +- + 5 files changed, 299 insertions(+), 13 deletions(-) + create mode 100644 ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_TopMetal1.lym + create mode 100644 ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_TopMetal2.lym + +diff --git a/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_ActGatP.lym b/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_ActGatP.lym +index a60a7ef..4cade7a 100644 +--- a/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_ActGatP.lym ++++ b/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_ActGatP.lym +@@ -149,6 +149,8 @@ + # CREATE Activ FILLER + ########################################################################## + ++ print(" -> Calculate Activ exclusion region\n") ++ + extent_all_layers = source.extent + Activ_cpy=Activ.dup + Poly_cpy=GatPoly.dup +@@ -188,6 +190,8 @@ + # CREATE GatPoly FILLER + ########################################################################## + ++ print(" -> Calculate GatPoly exclusion region\n") ++ + Activ_gp_cpy=Activ.dup + Poly_filler_cpy=GatPoly_filler.dup + Poly_gp_cpy=GatPoly.dup +@@ -233,7 +237,9 @@ + + org_x = 2000.0 + org_y = org_x ++ print(" -> Perform GatePoly fill\n") + (cellframe1-exclLayAct-exclLayGatP).fill(act_fill, hstep(spacing_h, offset_v), vstep(offset_h, spacing_v)) ++ print(" -> Perform Activ fill\n") + (cellframe1-exclLayGatP-exclLayAct).fill(gp_fill, hstep(gp_spacing_h, gp_offset_v), vstep(gp_offset_h, gp_spacing_v)) + + +diff --git a/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_Metal.lym b/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_Metal.lym +index 147cd1e..ff38154 100644 +--- a/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_Metal.lym ++++ b/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_Metal.lym +@@ -195,10 +195,10 @@ + end + + distances = { +- 'distance_m1' => 1.5, +- 'distance_m2' => 1.5, +- 'distance_m3' => 2.0, +- 'distance_m4' => 2.0, ++ 'distance_m1' => 0.42, ++ 'distance_m2' => 0.42, ++ 'distance_m3' => 0.42, ++ 'distance_m4' => 1.2, + 'distance_m5' => 2.0 + } + +@@ -259,6 +259,9 @@ + pattern_m5_s = fill_pattern("Met5_S_FILL_CELL").shape(67, 22, box(0.0, 0.0, width_m5_s, height_m5_s)) + pattern_m5_m = fill_pattern("Met5_M_FILL_CELL").shape(67, 22, box(0.0, 0.0, width_m5_m, height_m5_m)) + ++ ++ # This is my stupid way of hacking in multi_origin functionality as the true parameter seems to create off-grid structures. All values are pure guesses. ++ + print("Filling M1\n") + M1Fil_b = Metal1_filler.dup + M1Fil_b.size(0.42, 0.42, "square_limit") +@@ -267,8 +270,12 @@ + M1Fil_d = TRANS.dup + M1Fil_d.size(1.0, 1.0, "square_limit") + M1Fil = EdgeSeal.holes - (M1Fil_b | M1Fil_c | M1Fil_d | Metal1_nofill | Metal1_slit | TRANS | NoMetFiller) +- M1Fil_left = M1Fil.fill_with_left(pattern_m1_m, hstep(width_m1_m + distance_m1_m), vstep(height_m1_m + distance_m1_m)) +- M1Fil_left.fill(pattern_m1_s, hstep(width_m1_s + distance_m1_s), vstep(height_m1_s + distance_m1_s)) ++ m1Fil_left = M1Fil.fill_with_left(pattern_m1_m, hstep(width_m1_m + distance_m1_m), vstep(height_m1_m + distance_m1_m)) ++ for it in 0..24 do ++ origin_shift = (it * 0.07).round(2) ++ print(" -> Detail iteration #{it}: shift - #{origin_shift}\n") ++ m1Fil_left = m1Fil_left.fill_with_left(pattern_m1_s, hstep(width_m1_s + distance_m1_s), vstep(height_m1_s + distance_m1_s), origin(origin_shift, origin_shift)) ++ end + + print("Filling M2\n") + M2Fil_b = Metal2_filler.dup +@@ -278,8 +285,12 @@ + M2Fil_d = TRANS.dup + M2Fil_d.size(1.0, 1.0, "square_limit") + M2Fil = EdgeSeal.holes - (M2Fil_b | M2Fil_c | M2Fil_d | Metal2_nofill | Metal2_slit | TRANS | NoMetFiller) +- M2Fil_left = M2Fil.fill_with_left(pattern_m2_m, hstep(width_m2_m + distance_m2_m), vstep(height_m2_m + distance_m2_m)) +- M2Fil_left.fill(pattern_m2_s, hstep(width_m2_s + distance_m2_s), vstep(height_m2_s + distance_m2_s)) ++ m2Fil_left = M2Fil.fill_with_left(pattern_m2_m, hstep(width_m2_m + distance_m2_m), vstep(height_m2_m + distance_m2_m)) ++ for it in 0..24 do ++ origin_shift = (it * 0.07).round(2) ++ print(" -> Detail iteration #{it}: shift - #{origin_shift}\n") ++ m2Fil_left = m2Fil_left.fill_with_left(pattern_m2_s, hstep(width_m2_s + distance_m2_s), vstep(height_m2_s + distance_m2_s), origin(origin_shift, origin_shift)) ++ end + + print("Filling M3\n") + M3Fil_b = Metal3_filler.dup +@@ -289,8 +300,12 @@ + M3Fil_d = TRANS.dup + M3Fil_d.size(1.0, 1.0, "square_limit") + M3Fil = EdgeSeal.holes - (M3Fil_b | M3Fil_c | M3Fil_d | Metal3_nofill | Metal3_slit | TRANS | NoMetFiller) +- M3Fil_left = M3Fil.fill_with_left(pattern_m3_m, hstep(width_m3_m + distance_m3_m), vstep(height_m3_m + distance_m3_m)) +- M3Fil_left.fill(pattern_m3_s, hstep(width_m3_s + distance_m3_s), vstep(height_m3_s + distance_m3_s)) ++ m3Fil_left = M3Fil.fill_with_left(pattern_m3_m, hstep(width_m3_m + distance_m3_m), vstep(height_m3_m + distance_m3_m)) ++ for it in 0..24 do ++ origin_shift = (it * 0.07).round(2) ++ print(" -> Detail iteration #{it}: shift - #{origin_shift}\n") ++ m3Fil_left = m3Fil_left.fill_with_left(pattern_m3_s, hstep(width_m3_s + distance_m3_s), vstep(height_m3_s + distance_m3_s), origin(origin_shift, origin_shift)) ++ end + + print("Filling M4\n") + M4Fil_b = Metal4_filler.dup +diff --git a/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_TopMetal1.lym b/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_TopMetal1.lym +new file mode 100644 +index 0000000..a17cc9c +--- /dev/null ++++ b/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_TopMetal1.lym +@@ -0,0 +1,132 @@ ++ ++ ++ Fill TopMetal 1 ++ 0.1 ++ filler ++ ++ ++ ++ false ++ false ++ 0 ++ ++ true ++ filler ++ sg13g2_menu>end("SG13G2 PDK").end ++ dsl ++ drc-dsl-xml ++ ++ # Automatic filler generation script for TopMetal ++ Activ = source.input("1/0") ++ Activ_pin = source.input("1/2") ++ Activ_mask = source.input("1/20") ++ Activ_filler = source.input("1/22") ++ Activ_nofill = source.input("1/23") ++ BiWind = source.input("3/0") ++ GatPoly = source.input("5/0") ++ GatPoly_pin = source.input("5/2") ++ GatPoly_filler = source.input("5/22") ++ GatPoly_nofill = source.input("5/23") ++ Cont = source.input("6/0") ++ nSD = source.input("7/0") ++ nSD_block = source.input("7/21") ++ Metal1 = source.input("8/0") ++ Metal1_pin = source.input("8/2") ++ Metal1_filler = source.input("8/22") ++ Metal1_nofill = source.input("8/23") ++ Metal1_slit = source.input("8/24") ++ Passiv = source.input("9/0") ++ Metal2 = source.input("10/0") ++ Metal2_pin = source.input("10/2") ++ Metal2_filler = source.input("10/22") ++ Metal2_nofill = source.input("10/23") ++ Metal2_slit = source.input("10/24") ++ BasPoly = source.input("13/0") ++ pSD = source.input("14/0") ++ DigiBnd = source.input("16/0") ++ Via1 = source.input("19/0") ++ RES = source.input("24/0") ++ SRAM = source.input("25/0") ++ TRANS = source.input("26/0") ++ IND = source.input("27/0") ++ SalBlock = source.input("28/0") ++ Via2 = source.input("29/0") ++ Metal3 = source.input("30/0") ++ Metal3_pin = source.input("30/2") ++ Metal3_filler = source.input("30/22") ++ Metal3_nofill = source.input("30/23") ++ Metal3_slit = source.input("30/24") ++ NWell = source.input("31/0") ++ NWell_pin = source.input("31/2") ++ nBuLay = source.input("32/0") ++ nBuLay_block = source.input("32/21") ++ EmWind = source.input("33/0") ++ DeepCo = source.input("35/0") ++ MIM = source.input("36/0") ++ EdgeSeal = source.input("39/0") ++ dfpad = source.input("41/0") ++ dfpad_pillar = source.input("41/35") ++ dfpad_sbump = source.input("41/36") ++ ThickGateOx = source.input("44/0") ++ PWell = source.input("46/0") ++ PWell_block = source.input("46/21") ++ Via3 = source.input("49/0") ++ Metal4 = source.input("50/0") ++ Metal4_pin = source.input("50/2") ++ Metal4_filler = source.input("50/22") ++ Metal4_nofill = source.input("50/23") ++ Metal4_slit = source.input("50/24") ++ EmPoly = source.input("55/0") ++ DigiSub = source.input("60/0") ++ TEXT_0 = source.labels("63/0") ++ Via4 = source.input("66/0") ++ Metal5 = source.input("67/0") ++ Metal5_pin = source.input("67/2") ++ Metal5_filler = source.input("67/22") ++ Metal5_nofill = source.input("67/23") ++ Metal5_slit = source.input("67/24") ++ Polimide = source.input("98/0") ++ Recog = source.input("99/0") ++ Recog_esd = source.input("99/30") ++ Recog_diode = source.input("99/31") ++ Recog_tsv = source.input("99/32") ++ EXTBlock = source.input("111/0") ++ TopVia1 = source.input("125/0") ++ TopMetal1 = source.input("126/0") ++ TopMetal1_pin = source.input("126/2") ++ TopMetal1_filler = source.input("126/22") ++ TopMetal1_nofill = source.input("126/23") ++ TopMetal1_slit = source.input("126/24") ++ PolyRes = source.input("128/0") ++ Vmim = source.input("129/0") ++ TopVia2 = source.input("133/0") ++ TopMetal2 = source.input("134/0") ++ TopMetal2_pin = source.input("134/2") ++ TopMetal2_filler = source.input("134/22") ++ TopMetal2_nofill = source.input("134/23") ++ TopMetal2_slit = source.input("134/24") ++ ColWind = source.input("139/0") ++ RFMEM = source.input("147/0") ++ DeepVia = source.input("152/0") ++ LBE = source.input("157/0") ++ NoMetFiller = source.input("160/0") ++ ++ # Paramter ++ width = 5.0 ++ height = 10.0 ++ distance = 3.05 # exact value of 3.0, which is a min. value for TM1Fil.b causes random DRC violations ++ ++ # Create filler cell ++ pattern_tm1 = fill_pattern("TM1_FILL_CELL").shape(126, 22, box(0.0, 0.0, width, height)) ++ ++ # Define noFill exclusion layer ++ TM1Fil_c = TopMetal1.dup ++ TM1Fil_c.size(3.0, 3.0, "square_limit") ++ TM1Fil_d = TRANS.dup ++ TM1Fil_d.size(4.9, 4.9, "square_limit") ++ exclLayTM1 = TM1Fil_c | TM1Fil_d | TopMetal1_filler | TopMetal1_nofill | TopMetal1_slit | TRANS ++ ++ # perform fill ++ (EdgeSeal.holes - exclLayTM1).fill(pattern_tm1, hstep(width + distance), vstep(height + distance)) ++ ++ +diff --git a/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_TopMetal2.lym b/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_TopMetal2.lym +new file mode 100644 +index 0000000..58fc9ea +--- /dev/null ++++ b/ihp-sg13g2/libs.tech/klayout/tech/macros/sg13g2_filler_TopMetal2.lym +@@ -0,0 +1,131 @@ ++ ++ ++ Fill TopMetal 2 ++ 0.1 ++ filler ++ ++ ++ ++ false ++ false ++ 0 ++ ++ true ++ filler ++ sg13g2_menu>end("SG13G2 PDK").end ++ dsl ++ drc-dsl-xml ++ ++ # Automatic filler generation script for TopMetal ++ Activ = source.input("1/0") ++ Activ_pin = source.input("1/2") ++ Activ_mask = source.input("1/20") ++ Activ_filler = source.input("1/22") ++ Activ_nofill = source.input("1/23") ++ BiWind = source.input("3/0") ++ GatPoly = source.input("5/0") ++ GatPoly_pin = source.input("5/2") ++ GatPoly_filler = source.input("5/22") ++ GatPoly_nofill = source.input("5/23") ++ Cont = source.input("6/0") ++ nSD = source.input("7/0") ++ nSD_block = source.input("7/21") ++ Metal1 = source.input("8/0") ++ Metal1_pin = source.input("8/2") ++ Metal1_filler = source.input("8/22") ++ Metal1_nofill = source.input("8/23") ++ Metal1_slit = source.input("8/24") ++ Passiv = source.input("9/0") ++ Metal2 = source.input("10/0") ++ Metal2_pin = source.input("10/2") ++ Metal2_filler = source.input("10/22") ++ Metal2_nofill = source.input("10/23") ++ Metal2_slit = source.input("10/24") ++ BasPoly = source.input("13/0") ++ pSD = source.input("14/0") ++ DigiBnd = source.input("16/0") ++ Via1 = source.input("19/0") ++ RES = source.input("24/0") ++ SRAM = source.input("25/0") ++ TRANS = source.input("26/0") ++ IND = source.input("27/0") ++ SalBlock = source.input("28/0") ++ Via2 = source.input("29/0") ++ Metal3 = source.input("30/0") ++ Metal3_pin = source.input("30/2") ++ Metal3_filler = source.input("30/22") ++ Metal3_nofill = source.input("30/23") ++ Metal3_slit = source.input("30/24") ++ NWell = source.input("31/0") ++ NWell_pin = source.input("31/2") ++ nBuLay = source.input("32/0") ++ nBuLay_block = source.input("32/21") ++ EmWind = source.input("33/0") ++ DeepCo = source.input("35/0") ++ MIM = source.input("36/0") ++ EdgeSeal = source.input("39/0") ++ dfpad = source.input("41/0") ++ dfpad_pillar = source.input("41/35") ++ dfpad_sbump = source.input("41/36") ++ ThickGateOx = source.input("44/0") ++ PWell = source.input("46/0") ++ PWell_block = source.input("46/21") ++ Via3 = source.input("49/0") ++ Metal4 = source.input("50/0") ++ Metal4_pin = source.input("50/2") ++ Metal4_filler = source.input("50/22") ++ Metal4_nofill = source.input("50/23") ++ Metal4_slit = source.input("50/24") ++ EmPoly = source.input("55/0") ++ DigiSub = source.input("60/0") ++ TEXT_0 = source.labels("63/0") ++ Via4 = source.input("66/0") ++ Metal5 = source.input("67/0") ++ Metal5_pin = source.input("67/2") ++ Metal5_filler = source.input("67/22") ++ Metal5_nofill = source.input("67/23") ++ Metal5_slit = source.input("67/24") ++ Polimide = source.input("98/0") ++ Recog = source.input("99/0") ++ Recog_esd = source.input("99/30") ++ Recog_diode = source.input("99/31") ++ Recog_tsv = source.input("99/32") ++ EXTBlock = source.input("111/0") ++ TopVia1 = source.input("125/0") ++ TopMetal1 = source.input("126/0") ++ TopMetal1_pin = source.input("126/2") ++ TopMetal1_filler = source.input("126/22") ++ TopMetal1_nofill = source.input("126/23") ++ TopMetal1_slit = source.input("126/24") ++ PolyRes = source.input("128/0") ++ Vmim = source.input("129/0") ++ TopVia2 = source.input("133/0") ++ TopMetal2 = source.input("134/0") ++ TopMetal2_pin = source.input("134/2") ++ TopMetal2_filler = source.input("134/22") ++ TopMetal2_nofill = source.input("134/23") ++ TopMetal2_slit = source.input("134/24") ++ ColWind = source.input("139/0") ++ RFMEM = source.input("147/0") ++ DeepVia = source.input("152/0") ++ LBE = source.input("157/0") ++ NoMetFiller = source.input("160/0") ++ ++ # Paramter ++ width = 5.0 ++ height = 10.0 ++ distance = 3.05 # exact value of 3.0, which is a min. value for TM1Fil.b causes random DRC violations ++ ++ # Create filler cell ++ pattern_tm2 = fill_pattern("TM2_FILL_CELL").shape(134, 22, box(0.0, 0.0, width, height)) ++ ++ TM2Fil_c = TopMetal2.dup ++ TM2Fil_c.size(3.0, 3.0, "square_limit") ++ TM2Fil_d = TRANS.dup ++ TM2Fil_d.size(4.9, 4.9, "square_limit") ++ exclLayTM2 = TM2Fil_c | TM2Fil_d | TopMetal2_filler | TopMetal2_nofill | TopMetal2_slit | TRANS ++ ++ # perform fill ++ (EdgeSeal.holes - exclLayTM2).fill(pattern_tm2, hstep(width + distance), vstep(height + distance)) ++ ++ +diff --git a/ihp-sg13g2/libs.tech/klayout/tech/scripts/filler.py b/ihp-sg13g2/libs.tech/klayout/tech/scripts/filler.py +index 05a0fc7..47904bf 100644 +--- a/ihp-sg13g2/libs.tech/klayout/tech/scripts/filler.py ++++ b/ihp-sg13g2/libs.tech/klayout/tech/scripts/filler.py +@@ -12,7 +12,8 @@ This script has optional arguments to disable fill for some areas: + + * no_activ - Disable Activ and GatPoly fill + * no_metal - Disable Metal1 to Metal 5 fill +-* no_topmetal - Disable TopMetal1 and TopMetal2 fill ++* no_topmetal_1 - Disable TopMetal1 fill ++* no_topmetal_2 - Disable TopMetal2 fill + + These arguments don't take a value. See the following example. + +@@ -39,9 +40,10 @@ except NameError: + + NO_ACTIV = 'no_activ' in globals() + NO_METAL = 'no_metal' in globals() +-NO_TOPMETAL = 'no_topmetal' in globals() ++NO_TOPMETAL1 = 'no_topmetal_1' in globals() ++NO_TOPMETAL2 = 'no_topmetal_2' in globals() + +-scripts = [(NO_ACTIV, 'ActGatP'), (NO_METAL, 'Metal'), (NO_TOPMETAL, 'TopMetal')] ++scripts = [(NO_ACTIV, 'ActGatP'), (NO_METAL, 'Metal'), (NO_TOPMETAL1, 'TopMetal1'), (NO_TOPMETAL2, 'TopMetal2')] + + for disabled, area in scripts: + if disabled: +-- +2.39.3 + diff --git a/klayout/.gitignore b/klayout/.gitignore index 97314a93..c47e9b4d 100644 --- a/klayout/.gitignore +++ b/klayout/.gitignore @@ -1,2 +1,2 @@ .klayout -*.gds \ No newline at end of file +out diff --git a/klayout/def2gds.sh b/klayout/def2gds.sh deleted file mode 100755 index 1598dd5e..00000000 --- a/klayout/def2gds.sh +++ /dev/null @@ -1,89 +0,0 @@ -#! /bin/bash -# klayout batch mode for transferring def to gds - -export KLAYOUT=${KLAYOUT:-klayout} - -# this directory -klayout_dir=$(realpath $(dirname "${BASH_SOURCE[0]}")) -root_dir=$(dirname $klayout_dir) -cd $klayout_dir - -################ -### project ### -################ -top_design=${TOP_DESIGN:-"croc_chip"} -def_path=${DEF_PATH:-"$root_dir/openroad/out/croc.def"} - - -################ -## technology ## -################ -tech="$root_dir/ihp13/pdk/ihp-sg13g2/libs.tech/klayout/tech/sg13g2.lyt" -layer="$root_dir/ihp13/pdk/ihp-sg13g2/libs.tech/klayout/tech/sg13g2.lyp" - -# create klayout home dir and add pdk to path -export KLAYOUT_HOME="$klayout_dir/.klayout" -export KLAYOUT_PATH="$(realpath $root_dir/ihp13/pdk/ihp-sg13g2/libs.tech/klayout):$KLAYOUT_PATH" -mkdir -p $KLAYOUT_HOME/tech - - -if [[ -d "$root_dir/technology" ]]; then - echo "Init tech from ETHZ DZ cockpit" - pdk_dir=$(realpath "$root_dir/technology") - pdk_cells_lef_dir="${pdk_dir}/lef" - pdk_sram_lef_dir="${pdk_dir}/lef" - pdk_io_lef_dir="${pdk_dir}/lef" - pdk_cells_gds_dir="${pdk_dir}/gds" - pdk_sram_gds_dir="${pdk_dir}/gds" - pdk_io_gds_dir="${pdk_dir}/gds" -else - echo "Init tech from Github PDK" - pdk_dir=${pdk_dir:-$(realpath "$root_dir/ihp13/pdk/ihp-sg13g2")} - pdk_cells_lef_dir="${pdk_dir}/libs.ref/sg13g2_stdcell/lef" - pdk_sram_lef_dir="${pdk_dir}/libs.ref/sg13g2_sram/lef" - pdk_io_lef_dir="${pdk_dir}/libs.ref/sg13g2_io/lef" - pdk_cells_gds_dir="${pdk_dir}/libs.ref/sg13g2_stdcell/gds" - pdk_sram_gds_dir="${pdk_dir}/libs.ref/sg13g2_sram/gds" - pdk_io_gds_dir="${pdk_dir}/libs.ref/sg13g2_io/gds" -fi - -bondpad_lef_dir=$(realpath "$root_dir/ihp13/bondpad/lef") -bondpad_gds_dir=$(realpath "$root_dir/ihp13/bondpad/gds") - -lef="$(find "$pdk_cells_lef_dir" -name 'sg13g2_stdcell.lef' -exec realpath {} \;) \ - $(find "$pdk_cells_lef_dir" -name 'sg13g2_tech.lef' -exec realpath {} \;) \ - $(find "$pdk_sram_lef_dir" -name 'RM_IHPSG13*.lef' -exec realpath {} \;) \ - $(find "$pdk_io_lef_dir" -name 'sg13g2_io.lef' -exec realpath {} \;) \ - $(find "$bondpad_lef_dir" -name '*.lef' -exec realpath {} \;)" - -gds="$(find "$pdk_cells_gds_dir" -name 'sg13g2_stdcell.gds' -exec realpath {} \;) \ - $(find "$pdk_sram_gds_dir" -name 'RM_IHPSG13*.gds' -exec realpath {} \;) \ - $(find "$pdk_io_gds_dir" -name 'sg13g2_io.gds' -exec realpath {} \;) \ - $(find "$bondpad_gds_dir" -name '*.gds' -exec realpath {} \;)" - - -ln -sfr $klayout_dir/sg13g2.map $KLAYOUT_HOME/tech/sg13g2.map - -# all entries for the tech file -lef_files="" -for lef_file in $lef; do - lef_files+="$lef_file\n" -done - -# replace the placeholder tag with the real lef files -sed "/<\/lef-files>/c $lef_files" "$tech" > $KLAYOUT_HOME/tech/sg13g2.lyt - -echo "$gds" > $KLAYOUT_HOME/tech/tech_gds.f - - -klayout_cmd="$KLAYOUT -zz \ - -rd design_name=\"$top_design\" \ - -rd in_def=\"$def_path\" \ - -rd gds_flist=\"$KLAYOUT_HOME/tech/tech_gds.f\" \ - -rd out_file=\"${top_design}.gds\" \ - -rd tech_file=\"$KLAYOUT_HOME/tech/sg13g2.lyt\" \ - -rd layer_map=\"$KLAYOUT_HOME/tech/sg13g2.map\" \ - -rm def2stream.py" - -echo $klayout_cmd -eval $klayout_cmd diff --git a/klayout/def2stream.py b/klayout/scripts/def2stream.py similarity index 53% rename from klayout/def2stream.py rename to klayout/scripts/def2stream.py index b4ea2dce..6c3144f5 100644 --- a/klayout/def2stream.py +++ b/klayout/scripts/def2stream.py @@ -9,10 +9,9 @@ # Load technology file tech = pya.Technology() -tech.load(tech_file) layoutOptions = tech.load_layout_options -if len(layer_map) > 0: - layoutOptions.lefdef_config.map_file = layer_map +layoutOptions.lefdef_config.map_file = layer_map +layoutOptions.lefdef_config.lef_files = lef_files.split(' ') # Load def file main_layout = pya.Layout() @@ -21,7 +20,7 @@ print("[INFO] Reporting cells after loading DEF ...") for i in main_layout.each_cell(): - print("[INFO] '{0}'".format(i.name)) + print("[INFO] '{0}'".format(i.name)) # Clear cells top_cell_index = main_layout.cell(design_name).cell_index() @@ -30,17 +29,15 @@ # - KLayout is prepending VIA_ when reading DEF that instantiates LEF's via print("[INFO] Clearing cells...") for i in main_layout.each_cell(): - if i.cell_index() != top_cell_index: - if not i.name.startswith("VIA_"): - i.clear() + if i.cell_index() != top_cell_index: + if not i.name.startswith("VIA_"): + i.clear() # Load in the gds to merge print("[INFO] Merging GDS/OAS files...") -with open(gds_flist, "rb") as file: - in_files_list = file.read() -for fil in in_files_list.split(): - print("\t{0}".format(fil)) - main_layout.read(fil) +for fil in gds_files.split(): + print("\t{0}".format(fil)) + main_layout.read(fil) # Copy the top level only to a new layout print("[INFO] Copying toplevel cell '{0}'".format(design_name)) @@ -52,38 +49,31 @@ print("[INFO] Checking for missing cell from GDS/OAS...") missing_cell = False regex = None -if "GDS_ALLOW_EMPTY" in os.environ: - print("[INFO] Found GDS_ALLOW_EMPTY variable.") - regex = os.getenv("GDS_ALLOW_EMPTY") +if gds_allow_empty == 'True': + print("[INFO] Allow GDS_ALLOW_EMPTY.") for i in top_only_layout.each_cell(): - if i.is_empty(): - missing_cell = True - if regex is not None and re.match(regex, i.name): - print( - "[WARNING] LEF Cell '{0}' ignored. Matches GDS_ALLOW_EMPTY.".format( - i.name - ) - ) - else: - print( - "[ERROR] LEF Cell '{0}' has no matching GDS/OAS cell." - " Cell will be empty.".format(i.name) - ) - errors += 1 + if i.is_empty(): + missing_cell = True + if gds_allow_empty == 'True': + print("[WARNING] LEF Cell '{0}' ignored. Matches GDS_ALLOW_EMPTY.".format(i.name)) + else: + print("[ERROR] LEF Cell '{0}' has no matching GDS/OAS cell." + " Cell will be empty.".format(i.name)) + errors += 1 if not missing_cell: - print("[INFO] All LEF cells have matching GDS/OAS cells") + print("[INFO] All LEF cells have matching GDS/OAS cells") print("[INFO] Checking for orphan cell in the final layout...") orphan_cell = False for i in top_only_layout.each_cell(): - if i.name != design_name and i.parent_cells() == 0: - orphan_cell = True - print("[ERROR] Found orphan cell '{0}'".format(i.name)) - errors += 1 + if i.name != design_name and i.parent_cells() == 0: + orphan_cell = True + print("[ERROR] Found orphan cell '{0}'".format(i.name)) + errors += 1 if not orphan_cell: - print("[INFO] No orphan cells") + print("[INFO] No orphan cells") # Write out the GDS diff --git a/klayout/scripts/finishing.sh b/klayout/scripts/finishing.sh new file mode 100644 index 00000000..e92b0a1f --- /dev/null +++ b/klayout/scripts/finishing.sh @@ -0,0 +1,182 @@ +#! /bin/bash +# klayout batch mode to create the sealring and fill + +export KLAYOUT=${KLAYOUT:-klayout} + +root_dir=$(realpath $(dirname "${BASH_SOURCE[0]}")/../..) +klayout_dir=${root_dir}/ihp13/pdk/ihp-sg13g2/libs.tech/klayout +export KLAYOUT_PATH=$klayout_dir +export PDK=ihp-sg13g2 +export PDK_ROOT=${root_dir}/ihp13/pdk + +echo "Root: ${root_dir}" +echo "KLayout: ${klayout_dir}" + + +############### +### Filler ### +############### +FILLER=false + + +############# +### Help ### +############# +show_help() { + cat <&2 + echo + show_help + exit 1 + ;; + esac +done + + +################ +### Project ### +################ +top_design=${TOP_DESIGN:-"croc_chip"} +def_file=${DEF_FILE:-"$root_dir/openroad/out/croc.def"} +out_file=${OUT_FILE:-"$root_dir/klayout/out/$top_design.gds.gz"} + +out_path=$(realpath $(dirname "${out_file}")) +mkdir -p ${out_path} + +seal_file=${out_path}/${top_design}_sealring.gds.gz +sealed_file=${out_path}/${top_design}_sealed.gds.gz +filled_file=${out_path}/${top_design}_filled.gds.gz + +sealringspace=42 + +die_um=($(grep DIEAREA ${def_file} | grep -oE '[0-9]+' | tail -n 2 | xargs)) +die_width=$(( ${die_um[-2]} / 1000 )) +die_height=$(( ${die_um[-1]} / 1000 )) + +echo "Read ${def_file} - die area is ${die_width} um x ${die_height} um" + + +################ +## Technology ## +################ +if [[ -d "$root_dir/technology" ]]; then + echo "Init tech from ETHZ DZ cockpit" + pdk_dir=$(realpath "$root_dir/technology") + pdk_cells_lef_dir="${pdk_dir}/lef" + pdk_sram_lef_dir="${pdk_dir}/lef" + pdk_io_lef_dir="${pdk_dir}/lef" + pdk_cells_gds_dir="${pdk_dir}/gds" + pdk_sram_gds_dir="${pdk_dir}/gds" + pdk_io_gds_dir="${pdk_dir}/gds" +else + echo "Init tech from Github PDK" + pdk_dir=${pdk_dir:-$(realpath "$root_dir/ihp13/pdk/ihp-sg13g2")} + pdk_cells_lef_dir="${pdk_dir}/libs.ref/sg13g2_stdcell/lef" + pdk_sram_lef_dir="${pdk_dir}/libs.ref/sg13g2_sram/lef" + pdk_io_lef_dir="${pdk_dir}/libs.ref/sg13g2_io/lef" + pdk_cells_gds_dir="${pdk_dir}/libs.ref/sg13g2_stdcell/gds" + pdk_sram_gds_dir="${pdk_dir}/libs.ref/sg13g2_sram/gds" + pdk_io_gds_dir="${pdk_dir}/libs.ref/sg13g2_io/gds" +fi + +bondpad_lef_dir=$(realpath "$root_dir/ihp13/bondpad/lef") +bondpad_gds_dir=$(realpath "$root_dir/ihp13/bondpad/gds") + +lef="$(find "$pdk_cells_lef_dir" -name 'sg13g2_stdcell.lef' -exec realpath {} \;) \ + $(find "$pdk_cells_lef_dir" -name 'sg13g2_tech.lef' -exec realpath {} \;) \ + $(find "$pdk_sram_lef_dir" -name 'RM_IHPSG13*.lef' -exec realpath {} \;) \ + $(find "$pdk_io_lef_dir" -name 'sg13g2_io.lef' -exec realpath {} \;) \ + $(find "$bondpad_lef_dir" -name '*.lef' -exec realpath {} \;)" + +gds="$(find "$pdk_cells_gds_dir" -name 'sg13g2_stdcell.gds' -exec realpath {} \;) \ + $(find "$pdk_sram_gds_dir" -name 'RM_IHPSG13*.gds' -exec realpath {} \;) \ + $(find "$pdk_io_gds_dir" -name 'sg13g2_io.gds' -exec realpath {} \;) \ + $(find "$bondpad_gds_dir" -name '*.gds' -exec realpath {} \;)" + + +################ +## Def2Stream ## +################ +klayout_cmd="$KLAYOUT -zz \ + -rd gds_allow_empty="True" \ + -rd design_name=\"$top_design\" \ + -rd in_def=\"$def_file\" \ + -rd layer_map=\"$klayout_dir/tech/sg13g2.map\" \ + -rd lef_files=\"$lef\" \ + -rd gds_files=\"$gds\" \ + -rd out_file=\"$out_file\" \ + -rm scripts/def2stream.py" + +echo $klayout_cmd +eval $klayout_cmd + + +################# +### Seal Gen ### +################# +sealring_gen_cmd="$KLAYOUT -n sg13g2 -zz \ + -r $klayout_dir/tech/scripts/sealring.py \ + -rd width=$(( $die_width + 2 * $sealringspace )) \ + -rd height=$(( $die_width + 2 * $sealringspace )) \ + -rd output=$seal_file" + +echo $sealring_gen_cmd +eval $sealring_gen_cmd + + +################### +### Seal Merge ### +################### +sealring_merge_cmd="$KLAYOUT -zz \ + -rm $root_dir/klayout/scripts/merge_sealring.py \ + -rd chip_gds=$out_file \ + -rd seal_gds=$seal_file \ + -rd dx_um=$sealringspace \ + -rd dy_um=$sealringspace \ + -rd top_name=${chipname}_w_sealring \ + -rd out_gds=$sealed_file" + +echo $sealring_merge_cmd +eval $sealring_merge_cmd + + +####################### +### Density Filling ### +####################### +fill_cmd="$KLAYOUT -n sg13g2 -zz \ + -r $klayout_dir/tech/scripts/filler.py \ + -rd output_file=$filled_file \ + -rd no_topmetal_2 \ + $sealed_file" + +echo $fill_cmd +if [[ "$FILLER" == true ]]; then + eval $fill_cmd +fi diff --git a/klayout/scripts/merge_sealring.py b/klayout/scripts/merge_sealring.py new file mode 100644 index 00000000..91696812 --- /dev/null +++ b/klayout/scripts/merge_sealring.py @@ -0,0 +1,38 @@ +# Copyright 2025 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Thomas Benz +# Paul Scheffler +# Nils Wistoff +# Philippe Sauter + +"""Merges chip and a seal ring GDS""" + +import sys +import pya + + +chip = pya.Layout() +chip.read(chip_gds) +chip.read(seal_gds) +chip_top = chip.top_cells()[0] +seal_top = chip.top_cells()[1] + +top_lvl_cell = chip.create_cell(top_name) + +# calc shift +dx = int(round(float(dx_um) / chip.dbu)) +dy = int(round(float(dy_um) / chip.dbu)) + +# merge +top_lvl_cell.insert(pya.CellInstArray(chip_top.cell_index(), pya.Trans(pya.Vector(dx, dy)))) +top_lvl_cell.insert(pya.CellInstArray(seal_top.cell_index(), pya.Trans())) + +# stream out +tech = pya.Technology.technology_by_name("") +options = tech.save_layout_options +options.write_context_info = False +chip.write(out_gds, options=options) + +sys.exit(0) diff --git a/klayout/sg13g2.map b/klayout/sg13g2.map deleted file mode 100644 index 26143741..00000000 --- a/klayout/sg13g2.map +++ /dev/null @@ -1,154 +0,0 @@ -#************************************************************************# -#************************************************************************# -# File : sg13g2.map -# DATE : September 28, 2022 -#************************************************************************* -#************************************************************************* -# -# Copyright 2023 IHP 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. -# -#************************************************************************* -#------------------------------------------------------------------------- -# EDI Stream layer mapping table for SG13 -# Version: 1.0 -# Date: 22 Aug 2023 -# Use only in combination with valid GDSII data of all used blocks! -#------------------------------------------------------------------------- -#EDI Layer Name EDI Layer Type GDS Layer Number GDS Layer Type -#============== ============== ================ ============== - -Metal1 NET 8 0 -Metal1 SPNET 8 0 -Metal1 PIN 8 2 -Metal1 LEFPIN 8 2 -Metal1 FILL 8 22 -Metal1 LEFOBS 8 4 -Metal1 VIA 8 0 - -#NAME Metal1/NET 20 0 -#NAME Metal1/SPNET 20 0 -NAME Metal1/PIN 8 25 -#NAME Metal1/LEFPIN 20 0 - -Via1 PIN 19 0 -Via1 LEFPIN 19 0 -Via1 VIA 19 0 - -Metal2 NET 10 0 -Metal2 SPNET 10 0 -Metal2 PIN 10 2 -Metal2 LEFPIN 10 2 -Metal2 FILL 10 22 -Metal2 VIA 10 0 -Metal2 LEFOBS 10 4 - -#NAME Metal2/NET 21 0 -#NAME Metal2/SPNET 21 0 -NAME Metal2/PIN 10 25 -#NAME Metal2/LEFPIN 21 0 - -Via2 PIN 29 0 -Via2 LEFPIN 29 0 -Via2 VIA 29 0 - - -Metal3 NET 30 0 -Metal3 SPNET 30 0 -Metal3 PIN 30 2 -Metal3 LEFPIN 30 2 -Metal3 FILL 30 22 -Metal3 VIA 30 0 -Metal3 LEFOBS 30 4 - -#NAME Metal3/NET 22 0 -#NAME Metal3/SPNET 22 0 -NAME Metal3/PIN 30 25 -#NAME Metal3/LEFPIN 22 0 - -Via3 PIN 49 0 -Via3 LEFPIN 49 0 -Via3 VIA 49 0 - - -Metal4 NET 50 0 -Metal4 SPNET 50 0 -Metal4 PIN 50 2 -Metal4 LEFPIN 50 2 -Metal4 FILL 50 22 -Metal4 VIA 50 0 -Metal4 LEFOBS 50 4 - -#NAME Metal4/NET 23 0 -#NAME Metal4/SPNET 23 0 -NAME Metal4/PIN 50 25 -#NAME Metal4/LEFPIN 23 0 - -Via4 PIN 66 0 -Via4 LEFPIN 66 0 -Via4 VIA 66 0 - - -Metal5 NET 67 0 -Metal5 SPNET 67 0 -Metal5 PIN 67 2 -Metal5 LEFPIN 67 2 -Metal5 FILL 67 22 -Metal5 VIA 67 0 -Metal5 LEFOBS 67 4 - -#NAME Metal5/NET 70 0 -#NAME Metal5/SPNET 70 0 -NAME Metal5/PIN 67 25 -#NAME Metal5/LEFPIN 70 0 - -TopVia1 PIN 125 0 -TopVia1 LEFPIN 125 0 -TopVia1 VIA 125 0 - -TopMetal1 NET 126 0 -TopMetal1 SPNET 126 0 -TopMetal1 PIN 126 2 -TopMetal1 LEFPIN 126 2 -TopMetal1 FILL 126 22 -TopMetal1 VIA 126 0 -TopMetal1 LEFOBS 126 4 - -#NAME TopMetal1/NET 130 0 -#NAME TopMetal1/SPNET 130 0 -NAME TopMetal1/PIN 126 25 -#NAME TopMetal1/LEFPIN 130 0 - -TopVia2 PIN 133 0 -TopVia2 LEFPIN 133 0 -TopVia2 VIA 133 0 - -TopMetal2 NET 134 0 -TopMetal2 SPNET 134 0 -TopMetal2 PIN 134 2 -TopMetal2 LEFPIN 134 2 -TopMetal2 FILL 134 22 -TopMetal2 VIA 134 0 -TopMetal2 LEFOBS 135 4 - -#NAME TopMetal2/NET 137 0 -#NAME TopMetal2/SPNET 137 0 -NAME TopMetal2/PIN 134 25 -#NAME TopMetal2/LEFPIN 137 0 - -NAME COMP 63 0 - -COMP ALL 235 0 - -DIEAREA ALL 235 4