| # 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 density DRC runset output at: #{$report}") |
| report('GF180 DENSITY 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 density DRC runset output at default location: #{report_path}") |
| report('GF180 DENSITY 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 |
| |
| # METAL_TOP |
| METAL_TOP = $metal_top || '9K' |
| |
| logger.info("METAL_TOP Selected is #{METAL_TOP}") |
| |
| # METAL_LEVEL |
| METAL_LEVEL = $metal_level || '5LM' |
| |
| logger.info("METAL_STACK Selected is #{METAL_LEVEL}") |
| |
| #====================================================================================================== |
| #--------------------------------------- LAYER DEFINITIONS -------------------------------------------- |
| #====================================================================================================== |
| |
| polygons_count = 0 |
| logger.info('Read in polygons from layers.') |
| |
| def get_polygons(layer, data_type) |
| ps = polygons(layer, data_type) |
| $run_mode == 'deep' ? ps : ps.merged |
| end |
| |
| poly2 = get_polygons(30, 0) |
| count = poly2.count |
| logger.info("poly2 has #{count} polygons") |
| polygons_count += count |
| |
| metal1_drawn = get_polygons(34, 0) |
| count = metal1_drawn.count |
| logger.info("metal1_drawn has #{count} polygons") |
| polygons_count += count |
| |
| metal1_dummy = get_polygons(34, 4) |
| count = metal1_dummy.count |
| logger.info("metal1_dummy has #{count} polygons") |
| polygons_count += count |
| |
| metal1 = metal1_drawn + metal1_dummy |
| |
| via1 = get_polygons(35, 0) |
| count = via1.count |
| logger.info("via1 has #{count} polygons") |
| polygons_count += count |
| |
| metal2_drawn = get_polygons(36, 0) |
| count = metal2_drawn.count |
| logger.info("metal2_drawn has #{count} polygons") |
| polygons_count += count |
| |
| metal2_dummy = get_polygons(36, 4) |
| count = metal2_dummy.count |
| logger.info("metal2_dummy has #{count} polygons") |
| polygons_count += count |
| |
| metal2 = metal2_drawn + metal2_dummy |
| |
| if METAL_LEVEL == '2LM' |
| |
| top_via = via1 |
| topmin1_via = contact |
| top_metal = metal2 |
| topmin1_metal = metal1 |
| |
| else |
| |
| via2 = get_polygons(38, 0) |
| count = via2.count |
| logger.info("via2 has #{count} polygons") |
| polygons_count += count |
| |
| metal3_drawn = get_polygons(42, 0) |
| count = metal3_drawn.count |
| logger.info("metal3_drawn has #{count} polygons") |
| polygons_count += count |
| |
| metal3_dummy = get_polygons(42, 4) |
| count = metal3_dummy.count |
| logger.info("metal3_dummy has #{count} polygons") |
| polygons_count += count |
| |
| metal3 = metal3_drawn + metal3_dummy |
| |
| if METAL_LEVEL == '3LM' |
| |
| top_via = via2 |
| topmin1_via = via1 |
| top_metal = metal3 |
| topmin1_metal = metal2 |
| else |
| |
| via3 = get_polygons(40, 0) |
| count = via3.count |
| logger.info("via3 has #{count} polygons") |
| polygons_count += count |
| |
| metal4_drawn = get_polygons(46, 0) |
| count = metal4_drawn.count |
| logger.info("metal4_drawn has #{count} polygons") |
| polygons_count += count |
| |
| metal4_dummy = get_polygons(46, 4) |
| count = metal4_dummy.count |
| logger.info("metal4_dummy has #{count} polygons") |
| polygons_count += count |
| |
| metal4 = metal4_drawn + metal4_dummy |
| |
| if METAL_LEVEL == '4LM' |
| |
| top_via = via3 |
| topmin1_via = via2 |
| top_metal = metal4 |
| topmin1_metal = metal3 |
| else |
| |
| via4 = get_polygons(41, 0) |
| count = via4.count |
| logger.info("via4 has #{count} polygons") |
| polygons_count += count |
| |
| case METAL_LEVEL |
| when '5LM' |
| metal5_drawn = get_polygons(81, 0) |
| count = metal5_drawn.count |
| logger.info("metal5_drawn has #{count} polygons") |
| polygons_count += count |
| |
| metal5_dummy = get_polygons(81, 4) |
| count = metal5_dummy.count |
| logger.info("metal5_dummy has #{count} polygons") |
| polygons_count += count |
| |
| metal5 = metal5_drawn + metal5_dummy |
| |
| top_via = via4 |
| topmin1_via = via3 |
| top_metal = metal5 |
| topmin1_metal = metal4 |
| when '6LM' |
| metal5_drawn = get_polygons(81, 0) |
| count = metal5_drawn.count |
| logger.info("metal5_drawn has #{count} polygons") |
| polygons_count += count |
| |
| metal5_dummy = get_polygons(81, 4) |
| count = metal5_dummy.count |
| logger.info("metal5_dummy has #{count} polygons") |
| polygons_count += count |
| |
| metal5 = metal5_drawn + metal5_dummy |
| |
| via5 = get_polygons(82, 0) |
| count = via5.count |
| logger.info("via5 has #{count} polygons") |
| polygons_count += count |
| |
| metaltop_drawn = get_polygons(53, 0) |
| count = metaltop_drawn.count |
| logger.info("metaltop_drawn has #{count} polygons") |
| polygons_count += count |
| |
| metaltop_dummy = get_polygons(53, 4) |
| count = metaltop_dummy.count |
| logger.info("metaltop_dummy has #{count} polygons") |
| polygons_count += count |
| |
| metaltop = metaltop_drawn + metaltop_dummy |
| |
| top_via = via5 |
| topmin1_via = via4 |
| top_metal = metaltop |
| topmin1_metal = metal5 |
| else |
| logger.error("Unknown metal stack #{METAL_LEVEL}") |
| raise |
| end |
| end |
| end |
| end |
| |
| #====================================================================================================== |
| #--------------------------------------- LAYER DERIVATIONS -------------------------------------------- |
| #====================================================================================================== |
| |
| logger.info('Starting GF180ULL DENSITY layer derivations.') |
| |
| # === LAYOUT EXTENT === |
| CHIP = extent.sized(0.0) |
| |
| #===================================================================================================================== |
| #-------------------------------------------------- MAIN RUNSET ------------------------------------------------------ |
| #===================================================================================================================== |
| |
| logger.info('Starting GF180ULL DENSITY DRC rules.') |
| |
| # Rule PL.8: Poly2 coverage over the entire die shall be 14%. |
| ## Dummy poly2 lines must be added to meet the minimum poly2 density requirement. |
| logger.info('Executing rule PL.8') |
| if (poly2.area / CHIP.area) * 100 < 14 |
| poly2.output('PL.8', |
| 'PL.8 : Poly2 coverage over the entire die shall be 14%. |
| Dummy poly2 lines must be added to meet the minimum poly2 density requirement. : 14%') |
| end |
| |
| # Rule M1.4: Metal1 coverage over the entire die shall be >30% |
| ## (Refer to section 13.0 for Dummy Metal fill guidelines. |
| ## Customer needs to ensure enough dummy metal to satisfy Metal1 coverage) |
| logger.info('Executing rule M1.4') |
| if (metal1.area / CHIP.area) * 100 < 30 |
| metal1.output('M1.4', |
| 'M1.4 : Metal1 coverage over the entire die shall be >30% |
| (Refer to section 13.0 for Dummy Metal fill guidelines. |
| Customer needs to ensure enough dummy metal to satisfy Metal1 coverage) : 30%') |
| end |
| |
| # Rule M2.4: Metal2 coverage over the entire die shall be >30% |
| ## (Refer to section 13.0 for Dummy Metal fill guidelines. |
| ## Customer needs to ensure enough dummy metal to satisfy Metal2 coverage) |
| logger.info('Executing rule M2.4') |
| if (metal2.area / CHIP.area) * 100 < 30 |
| metal2.output('M2.4', |
| 'M2.4 : Metal2 coverage over the entire die shall be >30% |
| (Refer to section 13.0 for Dummy Metal fill guidelines. |
| Customer needs to ensure enough dummy metal to satisfy Metal2 coverage) : 30%') |
| end |
| |
| if METAL_LEVEL == '3LM' || METAL_LEVEL == '4LM' || METAL_LEVEL == '5LM' || METAL_LEVEL == '6LM' |
| # Rule M3.4: metal3 coverage over the entire die shall be >30% |
| ## (Refer to section 13.0 for Dummy Metal fill guidelines. |
| ## Customer needs to ensure enough dummy metal to satisfy metal3 coverage) |
| logger.info('Executing rule M3.4') |
| if (metal3.area / CHIP.area) * 100 < 30 |
| metal3.output('M3.4', |
| 'M3.4 : metal3 coverage over the entire die shall be >30% |
| (Refer to section 13.0 for Dummy Metal fill guidelines. |
| Customer needs to ensure enough dummy metal to satisfy metal3 coverage) : 30%') |
| end |
| end |
| |
| if METAL_LEVEL == '4LM' || METAL_LEVEL == '5LM' || METAL_LEVEL == '6LM' |
| # Rule M4.4: metal4 coverage over the entire die shall be >30% |
| ## (Refer to section 13.0 for Dummy Metal fill guidelines. |
| ## Customer needs to ensure enough dummy metal to satisfy metal4 coverage) |
| logger.info('Executing rule M4.4') |
| if (metal4.area / CHIP.area) * 100 < 30 |
| metal4.output('M4.4', |
| 'M4.4 : metal4 coverage over the entire die shall be >30% |
| (Refer to section 13.0 for Dummy Metal fill guidelines. |
| Customer needs to ensure enough dummy metal to satisfy metal4 coverage) : 30%') |
| end |
| end |
| |
| if METAL_LEVEL == '5LM' || METAL_LEVEL == '6LM' |
| # Rule M5.4: metal5 coverage over the entire die shall be >30% |
| ## (Refer to section 13.0 for Dummy Metal fill guidelines. |
| ## Customer needs to ensure enough dummy metal to satisfy metal5 coverage) |
| logger.info('Executing rule M5.4') |
| if (metal5.area / CHIP.area) * 100 < 30 |
| metal5.output('M5.4', |
| 'M5.4 : metal5 coverage over the entire die shall be >30% |
| (Refer to section 13.0 for Dummy Metal fill guidelines. |
| Customer needs to ensure enough dummy metal to satisfy metal5 coverage) : 30%') |
| end |
| end |
| |
| if METAL_TOP == '30K' |
| # Rule MT30.7: Thick MetalTop coverage over the entire die shall be >30% |
| ## (Refer to section 13.0 for Dummy Metal-fill guidelines. |
| ## Customer needs to ensure enough dummy metal to satisfy Metaln coverage). |
| logger.info('Executing rule MT30.7') |
| if (top_metal.area / CHIP.area) * 100 < 30 |
| top_metal.output('MT30.7', |
| 'MT30.7 : Thick MetalTop coverage over the entire die shall be >30% |
| (Refer to section 13.0 for Dummy Metal-fill guidelines. |
| Customer needs to ensure enough dummy metal to satisfy Metaln coverage). : 30%') |
| end |
| elsif METAL_TOP == '20K' |
| # Rule MT30.7: Thick MetalTop coverage over the entire die shall be >30% |
| ## (Refer to section 13.0 for Dummy Metal-fill guidelines. |
| ## Customer needs to ensure enough dummy metal to satisfy Metaln coverage). |
| logger.info('Executing rule MT20.7') |
| if (top_metal.area / CHIP.area) * 100 < 30 |
| top_metal.output('MT20.7', |
| 'MT20.7 : Thick MetalTop coverage over the entire die shall be >30% |
| (Refer to section 13.0 for Dummy Metal-fill guidelines. |
| Customer needs to ensure enough dummy metal to satisfy Metaln coverage). : 30%') |
| end |
| else |
| # Rule MT.3: MetalTop coverage over the entire die shall be >30% |
| ## (Refer to section 10.3 for Dummy Metal-fill guidelines. |
| ## Customer needs to ensure enough dummy metal to satisfy Metaln coverage) |
| logger.info('Executing rule MT.3') |
| if (top_metal.area / CHIP.area) * 100 < 30 |
| top_metal.output('MT.3', |
| 'MT.3 : MetalTop coverage over the entire die shall be >30% |
| (Refer to section 10.3 for Dummy Metal-fill guidelines. |
| Customer needs to ensure enough dummy metal to satisfy Metaln coverage) : 30%') |
| end |
| end |
| |
| exec_end_time = Time.now |
| run_time = exec_end_time - exec_start_time |
| logger.info(format('DRC Total Run time %f seconds', run_time)) |
| |
| #=================================== |
| #--------------- END --------------- |
| #=================================== |