| # Copyright 2022 GlobalFoundries 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. |
| |
| #======================================================================================================================= |
| #----------------------------------------- GF 0.18 um ULL DRC RULE DECK ------------------------------------------------ |
| #======================================================================================================================= |
| require 'time' |
| require 'logger' |
| require 'etc' |
| |
| exec_start_time = Time.now |
| |
| logger = Logger.new($stdout) |
| |
| logger.formatter = proc do |_severity, datetime, _progname, msg| |
| "#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10, 40].strip + ") : #{msg} |
| " |
| end |
| |
| #================================================ |
| #----------------- FILE SETUP ------------------- |
| #================================================ |
| logger.info("Starting running GF180ULL Klayout DRC runset on #{$input}") |
| logger.info("Ruby Version for klayout: #{RUBY_VERSION}") |
| |
| if $input |
| if $topcell |
| source($input, $topcell) |
| else |
| source($input) |
| end |
| end |
| |
| logger.info('Loading database to memory is complete.') |
| |
| if $report |
| logger.info("GF180ULL Klayout antenna DRC runset output at: #{$report}") |
| report('GF180 ANTENNA DRC Run Report at', $report) |
| else |
| layout_dir = Pathname.new(RBA::CellView.active.filename).parent.realpath |
| report_path = layout_dir.join('gf180_drc.lyrdb').to_s |
| logger.info("GF180ULL Klayout antenna DRC runset output at default location: #{report_path}") |
| report('GF180 ANTENNA DRC Run Report at', report_path) |
| end |
| |
| #======================================================================================= |
| #------------------------------------- SWITCHES ---------------------------------------- |
| #======================================================================================= |
| |
| logger.info('Evaluate switches.') |
| |
| # threads |
| if $thr |
| threads($thr) |
| else |
| thr ||= Etc.nprocessors |
| threads(thr) |
| end |
| |
| logger.info("Number of threads to use #{$thr}") |
| |
| #=== PRINT DETAILS === |
| logger.info("Verbose mode: #{$verbose}") |
| if $verbose == 'true' |
| verbose(true) |
| else |
| verbose(false) |
| end |
| |
| # === TILING MODE === |
| case $run_mode |
| when 'tiling' |
| tiles(500.um) |
| tile_borders(10.um) |
| logger.info('Tiling mode is enabled.') |
| |
| when 'deep' |
| #=== HIER MODE === |
| deep |
| logger.info('deep mode is enabled.') |
| else |
| #=== FLAT MODE === |
| flat |
| logger.info('flat mode is enabled.') |
| end |
| |
| #====================================================================================================== |
| #--------------------------------------- LAYER DEFINITIONS -------------------------------------------- |
| #====================================================================================================== |
| |
| |
| comp = polygons(22, 0).merged |
| lvpwell = polygons(204, 0 ).merged |
| dualgate = polygons(55, 0).merged |
| dualgate_2 = polygons(144, 0).merged |
| poly2 = polygons(30, 0).merged |
| nplus = polygons(32, 0).merged |
| pplus = polygons(31, 0).merged |
| contact = polygons(33, 0).merged |
| metal1 = polygons(34, 0).merged |
| via1 = polygons(35, 0).merged |
| metal2 = polygons(36, 0).merged |
| via2 = polygons(38, 0).merged |
| metal3 = polygons(42, 0).merged |
| via3 = polygons(40, 0).merged |
| metal4 = polygons(46, 0).merged |
| via4 = polygons(41, 0).merged |
| metal5 = polygons(81, 0).merged |
| via5 = polygons(82, 0).merged |
| metaltop = polygons(53, 0).merged |
| fusetop = polygons(75, 0).merged |
| v5_xtor = polygons(112, 1 ).merged |
| |
| #====================================================================================================== |
| #--------------------------------------- LAYER DERIVATIONS -------------------------------------------- |
| #====================================================================================================== |
| |
| tgate = poly2 & comp |
| thin_gate = tgate.not(dualgate_2).not(v5_xtor) |
| thick_gate = tgate.and(dualgate_2).not(dualgate) |
| diode_ull = nplus.and(comp).inside(lvpwell) |
| |
| |
| diode_1p8 = diode_ull.not(dualgate).not(dualgate_2).not(v5_xtor) |
| diode_3p3 = diode_ull.and(dualgate).not(dualgate_2).not(v5_xtor) |
| diode_6p0 = diode_ull.not(dualgate).and(dualgate_2).not(v5_xtor) |
| |
| diode = diode_1p8.or(diode_3p3).or(diode_6p0) |
| |
| |
| # === LAYOUT EXTENT === |
| CHIP = extent.sized(0.0) |
| |
| #======================================================================================= |
| #------------------------------------- SWITCHES ---------------------------------------- |
| #======================================================================================= |
| |
| logger.info('Evaluate switches.') |
| |
| if $mim_option |
| MIM_OPTION = $mim_option |
| else |
| MIM_OPTION = false |
| end |
| |
| logger.info('MIM Option selected %s' % [MIM_OPTION]) |
| |
| # METAL_TOP |
| if $metal_top |
| METAL_TOP = $metal_top |
| else |
| METAL_TOP = '9K' |
| end |
| |
| logger.info('METAL_TOP Selected is %s' % [METAL_TOP]) |
| |
| if METAL_TOP == '6K' |
| met_top_thick = 0.69.um |
| |
| elsif METAL_TOP == '9K' |
| met_top_thick = 0.99.um |
| |
| elsif METAL_TOP == '11K' |
| met_top_thick = 1.19.um |
| |
| elsif METAL_TOP == '30K' |
| met_top_thick = 3.035.um |
| |
| end #METAL_TOP |
| |
| if $metal_level |
| METAL_LEVEL = $metal_level |
| else |
| METAL_LEVEL = '6LM' |
| end |
| |
| logger.info('METAL_STACK Selected is %s' % [METAL_LEVEL]) |
| |
| if METAL_LEVEL == '6LM' |
| top_via = via5 |
| topmin1_via = via4 |
| top_metal = metaltop |
| topmin1_metal = metal5 |
| elsif METAL_LEVEL == '5LM' |
| top_via = via4 |
| topmin1_via = via3 |
| top_metal = metal5 |
| topmin1_metal = metal4 |
| elsif METAL_LEVEL == '4LM' |
| top_via = via3 |
| topmin1_via = via2 |
| top_metal = metal4 |
| topmin1_metal = metal3 |
| elsif METAL_LEVEL == '3LM' |
| top_via = via2 |
| topmin1_via = via1 |
| top_metal = metal3 |
| topmin1_metal = metal2 |
| elsif METAL_LEVEL == '2LM' |
| top_via = via1 |
| topmin1_via = via1 |
| top_metal = metal2 |
| topmin1_metal = metal1 |
| end #METAL_LEVEL |
| |
| #========================================================================================================================= |
| #---------------------------------------------------- MAIN RUNSET -------------------------------------------------------- |
| #========================================================================================================================= |
| |
| logger.info('Starting GF180ULL ANTENNA DRC rules.') |
| |
| #======================================== |
| #----------------- POLY ----------------- |
| #======================================== |
| connect(poly2,tgate ) |
| connect(poly2,thin_gate ) |
| connect(poly2,thick_gate) |
| # Rule ANT.1: Maximum ratio of Poly2 perimeter area to related gate oxide area is 200 |
| logger.info('Executing rule ANT.1') |
| antenna_check(tgate,perimeter_only(poly2,0.2.um), 200).output('ANT.1','ANT.1: Maximum ratio of Poly2 perimeter area to related gate oxide area is 200') |
| |
| #======================================== |
| #--------------- CONTACT ---------------- |
| #======================================== |
| connect(poly2,contact) |
| connect(diode,contact) |
| # Rule ANT.8: Maximum ratio of contact area to related gate oxide area is 10 |
| logger.info('Executing rule ANT.8') |
| antenna_check(tgate, contact, 10).output('ANT.8','ANT.8: Maximum ratio of contact area to related gate oxide area is 10') |
| |
| #======================================== |
| #---------------- METAL1 ---------------- |
| #======================================== |
| connect(contact,metal1) |
| # Rule ANT.2_thin: Diode filtering for ANT.2 [thin gate] , MF = 2 |
| logger.info('Executing rule ANT.2_thin') |
| antenna_check(thin_gate,perimeter_only(metal1,0.54.um), 400,[diode,800]).output('ANT.2_thin','ANT.2_thin: Maximum ratio of Metal1 perimeter area to related thin gate oxide area is 400') |
| |
| # Rule ANT.2_thick: Diode filtering for ANT.2 [thick gate] , MF = 15 |
| logger.info('Executing rule ANT.2_thick') |
| antenna_check(thick_gate,perimeter_only(metal1,0.54.um), 400,[diode,6000]).output('ANT.2_thick','ANT.2_thick: Maximum ratio of Metal1 perimeter area to related thick gate oxide area is 400') |
| |
| #======================================== |
| #----------------- VIA1 ----------------- |
| #======================================== |
| connect(metal1, via1 ) |
| # Rule ANT.9_thin: Diode filtering for ANT.9 [thin gate] |
| logger.info('Executing rule ANT.9_thin') |
| antenna_check(thin_gate,via1, 20,[diode,40]).output('ANT.9_thin','ANT.9_thin: Maximum ratio of Via1 area to related thin gate oxide area is 20') |
| |
| # Rule ANT.9_thick: Diode filtering for ANT.9 [thick gate] |
| logger.info('Executing rule ANT.9_thick') |
| antenna_check(thick_gate,via1, 20,[diode,300]).output('ANT.9_thick','ANT.9_thick: Maximum ratio of Via1 area to related thick gate oxide area is 20') |
| |
| #======================================== |
| #---------------- METAL2 ---------------- |
| #======================================== |
| connect(via1, metal2 ) |
| # Rule ANT.3_thin: Diode filtering for ANT.3 [thin gate] |
| logger.info('Executing rule ANT.3_thin') |
| antenna_check(thin_gate,perimeter_only(metal2,0.54.um), 400,[diode,800]).output('ANT.3_thin','ANT.3_thin: Maximum ratio of Metal2 perimeter area to related gate oxide area is 400') |
| |
| # Rule ANT.3_thick: Diode filtering for ANT.3 [thick gate] |
| logger.info('Executing rule ANT.3_thick') |
| antenna_check(thick_gate,perimeter_only(metal2,0.54.um), 400,[diode,6000]).output('ANT.3_thick','ANT.3_thick: Maximum ratio of Metal2 perimeter area to related gate oxide area is 400') |
| |
| #======================================== |
| #----------------- VIA2 ----------------- |
| #======================================== |
| connect(metal2, via2 ) |
| # Rule ANT.10_thin: Diode filtering for ANT.10 [thin gate] |
| logger.info('Executing rule ANT.10_thin') |
| antenna_check(thin_gate,via2, 20,[diode,40]).output('ANT.10_thin','ANT.10_thin: Maximum ratio of Via2 area to related thin gate oxide area is 20') |
| |
| # Rule ANT.10_thick: Diode filtering for ANT.10 [thick gate] |
| logger.info('Executing rule ANT.10_thick') |
| antenna_check(thick_gate,via2, 20,[diode,300]).output('ANT.10_thick','ANT.10_thick: Maximum ratio of Via2 area to related thick gate oxide area is 20') |
| |
| #======================================== |
| #---------------- METAL3 ---------------- |
| #======================================== |
| connect(via2, metal3 ) |
| # Rule ANT.4_thin: Diode filtering for ANT.4 [thin gate] |
| logger.info('Executing rule ANT.4_thin') |
| antenna_check(thin_gate,perimeter_only(metal3,0.54.um), 400,[diode,800]).output('ANT.4_thin','ANT.4_thin: Maximum ratio of Metal3 perimeter area to related gate oxide area is 400') |
| |
| # Rule ANT.4_thick: Diode filtering for ANT.4 [thick gate] |
| logger.info('Executing rule ANT.4_thick') |
| antenna_check(thick_gate,perimeter_only(metal3,0.54.um), 400,[diode,6000]).output('ANT.4_thick','ANT.4_thick: Maximum ratio of Metal3 perimeter area to related gate oxide area is 400') |
| |
| #======================== |
| #----- MIM OPTION A ----- |
| #======================== |
| if MIM_OPTION == 'A' |
| connect(metal3, fusetop ) |
| # Rule ANT.14_M3_MIMA: Maximum ratio of each of the metal3 layer perimeter area to related MIM area is 400 |
| logger.info('Executing rule ANT.14_M3_MIMA') |
| antenna_check(fusetop,perimeter_only(metal3,0.54.um), 400,[diode,6000]).output('ANT.14_M3_MIMA','ANT.14_M3_MIMA: Maximum ratio of each of the metal3 layer perimeter area to related MIM area is 400') |
| |
| # Rule ANT.15_V2_MIMA: Maximum ratio of each of Via2 area to related MIM area is 20 |
| logger.info('Executing rule ANT.15_V2_MIMA') |
| antenna_check(fusetop, via2, 20,[diode,300]).output('ANT.15_V2_MIMA','ANT.15_V2_MIMA: Maximum ratio of each of Via2 area to related MIM area is 20') |
| end |
| |
| #======================================== |
| #----------------- VIA3 ----------------- |
| #======================================== |
| connect(metal3, via3 ) |
| # Rule ANT.11_thin: Diode filtering for ANT.11 [thin gate] |
| logger.info('Executing rule ANT.11_thin') |
| antenna_check(thin_gate,via3, 20,[diode,40]).output('ANT.11_thin','ANT.11_thin: Maximum ratio of Via3 area to related thin gate oxide area is 20') |
| |
| # Rule ANT.11_thick: Diode filtering for ANT.11 [thick gate] |
| logger.info('Executing rule ANT.11_thick') |
| antenna_check(thick_gate,via3, 20,[diode,300]).output('ANT.11_thick','ANT.11_thick: Maximum ratio of Via3 area to related thick gate oxide area is 20') |
| |
| #======================== |
| #----- MIM OPTION A ----- |
| #======================== |
| if MIM_OPTION == 'A' |
| # Rule ANT.15_V3_MIMA: Maximum ratio of each of Via2 area to related MIM area is 20 |
| logger.info('Executing rule ANT.15_V3_MIMA') |
| antenna_check(fusetop, via3, 20,[diode,300]).output('ANT.15_V3_MIMA','ANT.15_V3_MIMA: Maximum ratio of each of Via3 area to related MIM area is 20') |
| end |
| |
| #======================================== |
| #---------------- METAL4 ---------------- |
| #======================================== |
| connect(via3, metal4 ) |
| # Rule ANT.5_thin: Diode filtering for ANT.5 [thin gate] |
| logger.info('Executing rule ANT.5_thin') |
| antenna_check(thin_gate,perimeter_only(metal4,0.54.um), 400,[diode,800]).output('ANT.5_thin','ANT.5_thin: Maximum ratio of Metal4 perimeter area to related gate oxide area is 400') |
| |
| # Rule ANT.5_thick: Diode filtering for ANT.5 [thick gate] |
| logger.info('Executing rule ANT.5_thick') |
| antenna_check(thick_gate,perimeter_only(metal4,0.54.um), 400,[diode,6000]).output('ANT.5_thick','ANT.5_thick: Maximum ratio of Metal4 perimeter area to related gate oxide area is 400') |
| |
| #======================== |
| #----- MIM OPTION A ----- |
| #======================== |
| if MIM_OPTION == 'A' |
| # Rule ANT.14_M4_MIMA: Maximum ratio of each of the metal3 layer perimeter area to related MIM area is 400 |
| logger.info('Executing rule ANT.15_V3_MIMA') |
| antenna_check(fusetop,perimeter_only(metal4,0.54.um), 400,[diode,6000]).output('ANT.14_M4_MIMA','ANT.14_M4_MIMA: Maximum ratio of each of the metal4 layer perimeter area to related MIM area is 400') |
| end |
| |
| #======================================== |
| #----------------- VIA4 ----------------- |
| #======================================== |
| connect(metal4, via4 ) |
| # Rule ANT.12_thin: Diode filtering for ANT.12 [thin gate] |
| logger.info('Executing rule ANT.12_thin') |
| antenna_check(thin_gate,via4, 20,[diode,40]).output('ANT.12_thin','ANT.12_thin: Maximum ratio of Via4 area to related thin gate oxide area is 20') |
| |
| # Rule ANT.12_thick: Diode filtering for ANT.12 [thick gate] |
| logger.info('Executing rule ANT.12_thick') |
| antenna_check(thick_gate,via4, 20,[diode,300]).output('ANT.12_thick','ANT.12_thick: Maximum ratio of Via4 area to related thick gate oxide area is 20') |
| |
| #======================== |
| #----- MIM OPTION A ----- |
| #======================== |
| if MIM_OPTION == 'A' |
| # Rule ANT.15_V4_MIMA: Maximum ratio of each of Via2 area to related MIM area is 20 |
| logger.info('Executing rule ANT.15_V4_MIMA') |
| antenna_check(fusetop, via4, 20,[diode,300]).output('ANT.15_V4_MIMA','ANT.15_V4_MIMA: Maximum ratio of each of Via4 area to related MIM area is 20') |
| end |
| |
| #======================================== |
| #---------------- METAL5 ---------------- |
| #======================================== |
| connect(via4, metal5 ) |
| # Rule ANT.6_thin: Diode filtering for ANT.6 [thin gate] |
| logger.info('Executing rule ANT.6_thin') |
| antenna_check(thin_gate,perimeter_only(metal5,0.54.um), 400,[diode,800]).output('ANT.6_thin','ANT.6_thin: Maximum ratio of Metal5 perimeter area to related gate oxide area is 400') |
| |
| # Rule ANT.6_thick: Diode filtering for ANT.6 [thick gate] |
| logger.info('Executing rule ANT.6_thick') |
| antenna_check(thick_gate,perimeter_only(metal5,0.54.um), 400,[diode,6000]).output('ANT.6_thick','ANT.6_thick: Maximum ratio of Metal5 perimeter area to related gate oxide area is 400') |
| |
| #======================== |
| #----- MIM OPTION A ----- |
| #======================== |
| if MIM_OPTION == 'A' |
| # Rule ANT.14_M5_MIMA: Maximum ratio of each of the metal3 layer perimeter area to related MIM area is 400 |
| logger.info('Executing rule ANT.14_M5_MIMA') |
| antenna_check(fusetop,perimeter_only(metal5,0.54.um), 400,[diode,6000]).output('ANT.14_M5_MIMA','ANT.14_M5_MIMA: Maximum ratio of each of the metal5 layer perimeter area to related MIM area is 400') |
| end |
| |
| #======================================== |
| #----------------- VIA5 ----------------- |
| #======================================== |
| connect(metal5, via5 ) |
| # Rule ANT.13_thin: Diode filtering for ANT.13 [thin gate] |
| logger.info('Executing rule ANT.13_thin') |
| antenna_check(thin_gate,via5, 20,[diode,40]).output('ANT.13_thin','ANT.13_thin: Maximum ratio of Via5 area to related thin gate oxide area is 20') |
| |
| # Rule ANT.13_thick: Diode filtering for ANT.13 [thick gate] |
| logger.info('Executing rule ANT.13_thick') |
| antenna_check(thick_gate,via5, 20,[diode,300]).output('ANT.13_thick','ANT.13_thick: Maximum ratio of Via5 area to related thick gate oxide area is 20') |
| |
| #======================== |
| #----- MIM OPTION A ----- |
| #======================== |
| if MIM_OPTION == 'A' |
| # Rule ANT.15_V5_MIMA: Maximum ratio of each of Via2 area to related MIM area is 20 |
| logger.info('Executing rule ANT.15_V5_MIMA') |
| antenna_check(fusetop, via5, 20,[diode,300]).output('ANT.15_V5_MIMA','ANT.15_V5_MIMA: Maximum ratio of each of Via5 area to related MIM area is 20') |
| end |
| |
| #======================================== |
| #--------------- METALTOP --------------- |
| #======================================== |
| connect(via5, metaltop) |
| # Rule ANT.7_thin: Diode filtering for ANT.7 [thin gate] |
| logger.info('Executing rule ANT.7_thin') |
| antenna_check(thin_gate,perimeter_only(metaltop,met_top_thick), 400,[diode,800]).output('ANT.7_thin','ANT.7_thin: Maximum ratio of Metaltop perimeter area to related gate oxide area is 400') |
| |
| # Rule ANT.7_thick: Diode filtering for ANT.7 [thick gate] |
| logger.info('Executing rule ANT.7_thick') |
| antenna_check(thick_gate,perimeter_only(metaltop,met_top_thick), 400,[diode,6000]).output('ANT.7_thick','ANT.7_thick: Maximum ratio of Metaltop perimeter area to related gate oxide area is 400') |
| |
| #======================== |
| #----- MIM OPTION A ----- |
| #======================== |
| if MIM_OPTION == 'A' |
| # Rule ANT.14_MT_MIMA: Maximum ratio of each of the metal3 layer perimeter area to related MIM area is 400 |
| logger.info('Executing rule ANT.14_MT_MIMA') |
| antenna_check(fusetop,perimeter_only(metaltop,met_top_thick), 400,[diode,6000]).output('ANT.14_MT_MIMA','ANT.14_MT_MIMA: Maximum ratio of each of the Metaltop layer perimeter area to related MIM area is 400') |
| end |
| |
| #======================== |
| #----- MIM OPTION B ----- |
| #======================== |
| if MIM_OPTION == 'B' |
| connect(metaltop, fusetop ) |
| # Rule ANT.14_MT_MIMB: Maximum ratio of each of the metal3 layer perimeter area to related MIM area is 400 |
| logger.info('Executing rule ANT.14_MT_MIMB') |
| antenna_check(fusetop,perimeter_only(metaltop,met_top_thick), 400,[diode,6000]).output('ANT.14_MT_MIMB','ANT.14_MT_MIMB: Maximum ratio of each of the metaltop layer perimeter area to related MIM area is 400') |
| end |
| |
| |
| exec_end_time = Time.now |
| run_time = exec_end_time - exec_start_time |
| logger.info('DRC Total Run time %f seconds' % [run_time]) |
| |
| |
| #=================================== |
| #--------------- END --------------- |
| #=================================== |
| |