| # DRC for SKY130 according to : |
| # https://skywater-pdk.readthedocs.io/en/latest/rules/periphery.html |
| # https://skywater-pdk.readthedocs.io/en/latest/rules/layers.html |
| # |
| # Distributed under GNU GPLv3: https://www.gnu.org/licenses/ |
| # |
| # History : |
| # 2022-01-13 : 2022.1.13_01.01 initial release |
| # |
| ########################################################################################## |
| |
| # optionnal for a batch launch : klayout -b -rd input=my_layout.gds -rd report=sky130_drc.txt -r drc_sky130.drc |
| if $input |
| source($input, $top_cell) |
| end |
| |
| if $report |
| report("SKY130 DRC runset", $report) |
| else |
| report("SKY130 DRC runset", File.join(File.dirname(RBA::CellView::active.filename), "sky130_drc.txt")) |
| end |
| |
| AL = true # do not change |
| CU = false # do not change |
| # choose betwen only one of AL or CU back-end flow here : |
| backend_flow = AL |
| |
| # enable / disable rule groups |
| if $feol == "1" || $feol == "true" |
| FEOL = true # front-end-of-line checks |
| else |
| FEOL = false |
| end |
| |
| if $beol == "1" || $beol == "true" |
| BEOL = true # back-end-of-line checks |
| else |
| BEOL = false |
| end |
| |
| if $offgrid == "1" || $offgrid == "true" |
| OFFGRID = true # manufacturing grid/angle checks |
| else |
| OFFGRID = false |
| end |
| |
| if $seal == "1" || $seal == "true" |
| SEAL = true # SEAL RING checks |
| else |
| SEAL = false |
| end |
| |
| if $floating_met == "1" || $floating_met == "true" |
| FLOATING_MET = true # back-end-of-line checks |
| else |
| FLOATING_MET = false |
| end |
| |
| # klayout setup |
| ######################## |
| # use a tile size of 1mm - not used in deep mode- |
| # tiles(1000.um) |
| # use a tile border of 10 micron: |
| # tile_borders(1.um) |
| #no_borders |
| |
| # hierachical |
| deep |
| |
| if $thr |
| threads($thr) |
| else |
| threads(4) |
| end |
| |
| # if more inof is needed, set true |
| # verbose(true) |
| verbose(true) |
| |
| # layers definitions |
| ######################## |
| |
| # all except purpose (datatype) 5 -- label and 44 -- via |
| li_wildcard = "67/20" |
| mcon_wildcard = "67/44" |
| |
| m1_wildcard = "68/20" |
| via_wildcard = "68/44" |
| |
| m2_wildcard = "69/20" |
| via2_wildcard = "69/44" |
| |
| m3_wildcard = "70/20" |
| via3_wildcard = "70/44" |
| |
| m4_wildcard = "71/20" |
| via4_wildcard = "71/44" |
| |
| m5_wildcard = "72/20" |
| |
| diff = input(65, 20) |
| tap = polygons(65, 44) |
| nwell = polygons(64, 20) |
| dnwell = polygons(64, 18) |
| pwbm = polygons(19, 44) |
| pwde = polygons(124, 20) |
| natfet = polygons(124, 21) |
| hvtr = polygons(18, 20) |
| hvtp = polygons(78, 44) |
| ldntm = polygons(11, 44) |
| hvi = polygons(75, 20) |
| tunm = polygons(80, 20) |
| lvtn = polygons(125, 44) |
| poly = polygons(66, 20) |
| hvntm = polygons(125, 20) |
| nsdm = polygons(93, 44) |
| psdm = polygons(94, 20) |
| rpm = polygons(86, 20) |
| urpm = polygons(79, 20) |
| npc = polygons(95, 20) |
| licon = polygons(66, 44) |
| |
| li = polygons(li_wildcard) |
| mcon = polygons(mcon_wildcard) |
| |
| m1 = polygons(m1_wildcard) |
| via = polygons(via_wildcard) |
| |
| m2 = polygons(m2_wildcard) |
| via2 = polygons(via2_wildcard) |
| |
| m3 = polygons(m3_wildcard) |
| via3 = polygons(via3_wildcard) |
| |
| m4 = polygons(m4_wildcard) |
| via4 = polygons(via4_wildcard) |
| |
| m5 = polygons(m5_wildcard) |
| |
| pad = polygons(76, 20) |
| nsm = polygons(61, 20) |
| capm = polygons(89, 44) |
| cap2m = polygons(97, 44) |
| vhvi = polygons(74, 21) |
| uhvi = polygons(74, 22) |
| npn = polygons(82, 20) |
| inductor = polygons(82, 24) |
| vpp = polygons(82, 64) |
| pnp = polygons(82, 44) |
| lvs_prune = polygons(84, 44) |
| ncm = polygons(92, 44) |
| padcenter = polygons(81, 20) |
| mf = polygons(76, 44) |
| areaid_sl = polygons(81, 1) |
| areaid_ce = polygons(81, 2) |
| areaid_fe = polygons(81, 3) |
| areaid_sc = polygons(81, 4) |
| areaid_sf = polygons(81, 6) |
| areaid_sw = polygons(81, 7) |
| areaid_sr = polygons(81, 8) |
| areaid_mt = polygons(81, 10) |
| areaid_dt = polygons(81, 11) |
| areaid_ft = polygons(81, 12) |
| areaid_ww = polygons(81, 13) |
| areaid_ld = polygons(81, 14) |
| areaid_ns = polygons(81, 15) |
| areaid_ij = polygons(81, 17) |
| areaid_zr = polygons(81, 18) |
| areaid_ed = polygons(81, 19) |
| areaid_de = polygons(81, 23) |
| areaid_rd = polygons(81, 24) |
| areaid_dn = polygons(81, 50) |
| areaid_cr = polygons(81, 51) |
| areaid_cd = polygons(81, 52) |
| areaid_st = polygons(81, 53) |
| areaid_op = polygons(81, 54) |
| areaid_en = polygons(81, 57) |
| areaid_en20 = polygons(81, 58) |
| areaid_le = polygons(81, 60) |
| areaid_hl = polygons(81, 63) |
| areaid_sd = polygons(81, 70) |
| areaid_po = polygons(81, 81) |
| areaid_it = polygons(81, 84) |
| areaid_et = polygons(81, 101) |
| areaid_lvt = polygons(81, 108) |
| areaid_re = polygons(81, 125) |
| areaid_ag = polygons(81, 79) |
| poly_rs = polygons(66, 13) |
| diff_rs = polygons(65, 13) |
| pwell_rs = polygons(64, 13) |
| li_rs = polygons(67, 13) |
| cfom = polygons(22, 20) |
| |
| |
| # Define a new custom function that selects polygons by their number of holes: |
| # It will return a new layer containing those polygons with min to max holes. |
| # max can be nil to omit the upper limit. |
| class DRC::DRCLayer |
| def with_holes(min, max) |
| new_data = RBA::Region::new |
| self.data.each do |p| |
| if p.holes >= (min || 0) && (!max || p.holes <= max) |
| new_data.insert(p) |
| end |
| end |
| DRC::DRCLayer::new(@engine, new_data) |
| end |
| end |
| |
| # DRC section |
| ######################## |
| log("DRC section") |
| |
| if FEOL |
| log("FEOL section") |
| |
| # nsdm |
| log("START: 93/44 (nsdm)") |
| nsdm.and(psdm).output("nsd.1", "nsd.1 : nsdm should not overlap psdm") |
| log("END: 93/44 (nsdm)") |
| |
| end #FEOL |