mirror of
https://github.com/TinyTapeout/tt-support-tools.git
synced 2026-11-03 12:46:08 +01:00
Previous fix caused issue with resistors since that check must not apply to anything in prec_res zone according to licon.9 Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
1774 lines
59 KiB
Plaintext
1774 lines
59 KiB
Plaintext
# 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-6-22 : 2022.6.30_01.07 release
|
||
#
|
||
# 2023-6-14 : 2023.6.14_01.08 release
|
||
#
|
||
# 2024-2-11 : 2024.2.11_01.09 release
|
||
#
|
||
# 2024-5-26 : Update rectMCON_peri to use outside instead of not
|
||
# 2024-8-15 :
|
||
# * Avoid assigning to constant variables
|
||
# * Fix defaults for arguments when they are not passed in the CLI
|
||
#
|
||
# 2024-8-18 :
|
||
# * Add new rules:
|
||
# * `MR_lvtn.OVL.2`
|
||
# * `MR_tunm.CON.1`
|
||
# * `rdl.1`
|
||
# * `rdl.2`
|
||
# * `MR_thkox.CON.1`
|
||
# * `MR_rdl.CON.1`
|
||
# * `MR_li.WID.4`
|
||
# * `moduleCut.1`
|
||
# * Modify `licon` checks to account for ("non-bar" | exempt prec_resistor)
|
||
# * Rename `licon` checks
|
||
# 2024-9-1 :
|
||
# * Change the default of sram_exclude to false
|
||
# 2025-10-29 : 2025.10.29_01.10 release
|
||
# * Updated to be in line with the current Skywater required rules
|
||
##########################################################################################
|
||
release = "2025.10.29_01.10"
|
||
|
||
require "time"
|
||
require "logger"
|
||
|
||
exec_start_time = Time.now
|
||
|
||
logger = Logger.new(STDOUT)
|
||
|
||
logger.formatter =
|
||
proc do |severity, datetime, progname, msg|
|
||
"#{msg}
|
||
"
|
||
end
|
||
# optionnal for a batch launch : klayout -b -rd input=my_layout.gds -rd report=sky130_drc.txt -r drc_sky130.drc
|
||
source($input, $top_cell) if $input
|
||
|
||
if $report
|
||
report("SKY130 DRC runset", $report)
|
||
else
|
||
report(
|
||
"SKY130 DRC runset",
|
||
File.join(File.dirname(RBA::CellView.active.filename), "sky130_drc.txt")
|
||
)
|
||
end
|
||
|
||
if not defined? $feol
|
||
$feol = "false"
|
||
end
|
||
if not defined? $beol
|
||
$beol = "false"
|
||
end
|
||
if not defined? $offgrid
|
||
$offgrid = "false"
|
||
end
|
||
if not defined? $floating_met
|
||
$floating_met = "false"
|
||
end
|
||
if not defined? $sram_exclude
|
||
$sram_exclude = "false"
|
||
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 == "0" || $feol == "false"
|
||
FEOL = false
|
||
else
|
||
FEOL = true # front-end-of-line checks
|
||
end
|
||
|
||
if $beol == "0" || $beol == "false"
|
||
BEOL = false
|
||
else
|
||
BEOL = true # back-end-of-line checks
|
||
end
|
||
|
||
if $offgrid == "0" || $offgrid == "false"
|
||
OFFGRID = false
|
||
else
|
||
OFFGRID = true # manufacturing grid/angle checks
|
||
end
|
||
|
||
if $seal == "0" || $seal == "false"
|
||
SEAL = false
|
||
else
|
||
SEAL = true # SEAL RING checks
|
||
end
|
||
|
||
if $floating_met == "0" || $floating_met == "false"
|
||
FLOATING_MET = false
|
||
else
|
||
FLOATING_MET = true # back-end-of-line checks
|
||
end
|
||
|
||
if $sram_exclude == "0" || $sram_exclude == "false"
|
||
SRAM_EXCLUDE = false # back-end-of-line checks
|
||
else
|
||
SRAM_EXCLUDE = true
|
||
end
|
||
|
||
if $info == "0" || $info == "false"
|
||
INFO = false
|
||
else
|
||
INFO = true # front-end-of-line checks
|
||
end
|
||
|
||
logger.info("Args:")
|
||
logger.info("\tsram_exclude: #{$sram_exclude}")
|
||
logger.info("\tfeol: #{$feol}")
|
||
logger.info("\tbeol: #{$beol}")
|
||
logger.info("\tfloating_met: #{$floating_met}")
|
||
logger.info("\toffgrid: #{$offgrid}")
|
||
logger.info("\tseal: #{$seal}")
|
||
|
||
# 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
|
||
|
||
$thr ? threads($thr) : threads(4)
|
||
|
||
# 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"
|
||
li_res_wildcard = "67/13"
|
||
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"
|
||
|
||
nsdm_wildcard = "93/44"
|
||
|
||
psdm_wildcard = "94/20"
|
||
nwell_wildcard = "64/20"
|
||
|
||
diff = input(65, 20)
|
||
tap = polygons(65, 44)
|
||
nwell = polygons(nwell_wildcard)
|
||
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(nsdm_wildcard)
|
||
psdm = polygons(psdm_wildcard)
|
||
rpm = polygons(86, 20)
|
||
urpm = polygons(79, 20)
|
||
npc = polygons(95, 20)
|
||
licon = polygons(66, 44)
|
||
|
||
li = polygons(li_wildcard)
|
||
li_res = polygons(li_res_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)
|
||
rdl = polygons(74, 20)
|
||
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)
|
||
poly_fill = polygons(66, 99)
|
||
diff_rs = polygons(65, 13)
|
||
pwell_rs = polygons(64, 13)
|
||
li_rs = polygons(67, 13)
|
||
cfom = polygons(22, 20)
|
||
thkox = polygons(75, 21)
|
||
|
||
areaid_ce_merged = areaid_ce.merged
|
||
|
||
# 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|
|
||
new_data.insert(p) if p.holes >= (min || 0) && (!max || p.holes <= max)
|
||
end
|
||
DRC::DRCLayer.new(@engine, new_data)
|
||
end
|
||
end
|
||
|
||
# DRC section
|
||
########################
|
||
log("DRC section")
|
||
|
||
if FEOL
|
||
log("FEOL section")
|
||
# dnwell
|
||
log("START: 64/18 (dnwell)")
|
||
dnwell
|
||
.width(3.0, euclidian)
|
||
.output(
|
||
"MRW_dnwell.WID.1",
|
||
"MRW_dnwell.WID.1: min. dnwell width : 3.0um"
|
||
)
|
||
dnwell
|
||
.not(uhvi.or(vhvi))
|
||
.space(6.3, euclidian)
|
||
.output(
|
||
"MRW_dnwell.SP.1",
|
||
"MRW_dnwell.SP.1 : min. dnwell spacing : 6.3um"
|
||
)
|
||
log("END: 64/18 (dnwell)")
|
||
|
||
not_sram = layout(source.cell_obj).select("-*sky130_sram_*kbyte_*")
|
||
not_sram_nsdm = not_sram.input(nsdm_wildcard)
|
||
not_sram_psdm = not_sram.input(psdm_wildcard)
|
||
not_sram_nwell = not_sram.input(nwell_wildcard)
|
||
|
||
# This is a hack, should be reverted
|
||
|
||
not_io =
|
||
layout(source.cell_obj).select(
|
||
"-*sky130_fd_io__gpiov2_amux",
|
||
"-*sky130_fd_io__simple_pad_and_busses"
|
||
)
|
||
not_io_nwell = not_io.input(nwell_wildcard)
|
||
|
||
# nwell
|
||
log("START: 64/20 (nwell)")
|
||
nwell.width(0.84, euclidian).output(
|
||
"MR_nwell.WID.1",
|
||
"MR_nwell.WID.1 : min. nwell width : 0.84um"
|
||
)
|
||
nwell.space(1.27, euclidian).output(
|
||
"MR_nwell.SP.1",
|
||
"MR_nwell.SP.1 : min. nwell spacing (merged if less) : 1.27um"
|
||
)
|
||
nwell_interact = not_sram_nwell.and(not_io_nwell).merge
|
||
dnwell.enclosing(nwell_interact.holes, 1.03, euclidian).output(
|
||
"MR_nwell.ENC.1",
|
||
"MR_nwell.ENC.1 : min enclosure of nwellHole by dnwell : 1.03um"
|
||
)
|
||
hvmarker = hvi.or(rdl).or(uhvi).or(vhvi)
|
||
nwell
|
||
.interacting(nwell.and(hvmarker))
|
||
.not(hvmarker)
|
||
.output("MR_nwell.CON.1", "MR_nwell.CON.1 : HVnwell must be enclosed by hv marker")
|
||
log("END: 64/20 (nwell)")
|
||
|
||
# hvtp
|
||
log("START: 78/44 (hvtp)")
|
||
hvtp.width(0.38, euclidian).output(
|
||
"MR_hvtp.WID.1",
|
||
"MR_hvtp.WID.1 : min. hvtp width : 0.38um"
|
||
)
|
||
hvtp.space(0.38, euclidian).output(
|
||
"MR_hvtp.SP.1",
|
||
"MR_hvtp.SP.1 : min. hvtp spacing : 0.38um"
|
||
)
|
||
log("END: 78/44 (hvtp)")
|
||
|
||
# pwde
|
||
log("START: 124/20 (pwde)")
|
||
pwde.width(0.84, euclidian).output(
|
||
"pwde.1",
|
||
"pwde.1 : min. pwde width : 0.84um"
|
||
)
|
||
pwde
|
||
.and(uhvi.or(vhvi))
|
||
.space(1.27, euclidian)
|
||
.output("MR_pwde.SP.1", "MR_pwde.SP.1 : min. pwde inside v20 spacing : 1.27um")
|
||
log("END: 124/20 (pwde)")
|
||
|
||
# hvtr
|
||
log("START: 18/20 (htvr)")
|
||
hvtr.width(0.38, euclidian).output(
|
||
"hvtr.1",
|
||
"hvtr.1 : min. hvtr width : 0.38um"
|
||
)
|
||
hvtr.separation(hvtp, 0.38, euclidian).output(
|
||
"hvtr.2",
|
||
"hvtr.2 : min. hvtr spacing : 0.38um"
|
||
)
|
||
hvtr.and(hvtp).output("MR_hvtr.CON.1", "MR_hvtr.CON.1 : hvtr must not overlap hvtp")
|
||
log("END: 18/20 (htvr)")
|
||
|
||
# lvtn
|
||
log("START: 25/44 (lvtn)")
|
||
lvtn.width(0.38, euclidian).output(
|
||
"lvtn.1a",
|
||
"lvtn.1a : min. lvtn width : 0.38um"
|
||
)
|
||
lvtn.space(0.38, euclidian).output(
|
||
"lvtn.2",
|
||
"lvtn.2 : min. lvtn spacing : 0.38um"
|
||
)
|
||
lvtn
|
||
.interacting(nwell)
|
||
.not_inside(nwell)
|
||
.edges
|
||
.and(nwell)
|
||
.output("MR_lvtn.OVL.2", "MR_lvtn.OVL.2 : lvtn must not straddle nwell")
|
||
log("END: 25/44 (lvtn)")
|
||
|
||
# ncm
|
||
log("START: 92/44 (ncm)")
|
||
ncm.width(0.38, euclidian).output("MR_ncm.WID.1", "MR_ncm.WID.1 : min. ncm width : 0.38um")
|
||
ncm.space(0.38, euclidian).output(
|
||
"ncm.2a",
|
||
"ncm.2a : min. ncm spacing : 0.38um"
|
||
)
|
||
log("END: 92/44 (ncm)")
|
||
|
||
# diff-tap
|
||
log("START: 65/20 (diff)")
|
||
difftap = diff.or(tap)
|
||
diff_width = diff.rectangles.width(0.15, euclidian).polygons
|
||
diff_cross_areaid_ce =
|
||
diff_width
|
||
.edges
|
||
.outside_part(areaid_ce)
|
||
.not(diff_width.outside(areaid_ce).edges)
|
||
diff_cross_areaid_ce.output(
|
||
"difftap.1",
|
||
"difftap.1 : min. diff width across areaid:ce : 0.15um"
|
||
)
|
||
diff
|
||
.outside(areaid_ce)
|
||
.width(0.15, euclidian)
|
||
.output(
|
||
"difftap.1_a",
|
||
"difftap.1_a : min. diff width in periphery : 0.15um"
|
||
)
|
||
diff
|
||
.inside(areaid_ce)
|
||
.width(0.14, euclidian)
|
||
.output(
|
||
"difftap.2",
|
||
"difftap.2 : min. diff width inside areadid:ce : 0.14um"
|
||
)
|
||
log("END: 65/20 (diff)")
|
||
|
||
log("START: 65/44 (tap)")
|
||
tap_width = tap.rectangles.width(0.15, euclidian).polygons
|
||
tap_cross_areaid_ce =
|
||
tap_width
|
||
.edges
|
||
.outside_part(areaid_ce)
|
||
.not(tap_width.outside(areaid_ce).edges)
|
||
tap_cross_areaid_ce.output(
|
||
"difftap.1_b",
|
||
"difftap.1_b : min. tap width across areaid:ce : 0.15um"
|
||
)
|
||
tap
|
||
.not(areaid_ce)
|
||
.width(0.15, euclidian)
|
||
.output("MR_diff.WID.2", "MR_diff.WID.2 : min. tap width in periphery : 0.15um")
|
||
log("END: 65/44 (tap)")
|
||
|
||
difftap.space(0.27, euclidian).output(
|
||
"difftap.3",
|
||
"difftap.3 : min. difftap spacing : 0.27um"
|
||
)
|
||
|
||
# tunm
|
||
log("START: 80/20 (tunm)")
|
||
tunm.width(0.41, euclidian).output(
|
||
"tunm.1",
|
||
"tunm.1 : min. tunm width : 0.41um"
|
||
)
|
||
tunm.space(0.5, euclidian).output(
|
||
"tunm.2",
|
||
"tunm.2 : min. tunm spacing : 0.5um"
|
||
)
|
||
tunm.output(
|
||
"MR_tunm.CON.1",
|
||
"MR_tunm.CON.1 : use of layer tunm is prohibited"
|
||
)
|
||
log("END: 80/20 (tunm)")
|
||
|
||
# thkox
|
||
log("START: 80/20 (thkox)")
|
||
thkox_peri = thkox.not(areaid_ce)
|
||
thkox_peri.width(0.6, euclidian).output(
|
||
"thkox.1",
|
||
"thkox.1 : min. thkox width inside periphery : 0.6um"
|
||
)
|
||
thkox_peri.space(0.7, euclidian).output(
|
||
"thkox.2",
|
||
"thkox.2 : min. thkox spacing inside periphery : 0.7um"
|
||
)
|
||
# thkox
|
||
hvi
|
||
.interacting(diff)
|
||
.not_inside(diff)
|
||
.edges
|
||
.and(diff)
|
||
.output("MR_thkox.CON.1", "MR_thkox.CON.1 : thkox must not straddle diff")
|
||
|
||
# MR_hv.SP.10: ndiff inside (v5, v12, or v20 and thkox) minimum spacing when non-abutting ptap
|
||
# Note: Using available area identifiers - areaid_ft (81,12) for v12, areaid_ed (81,19) for v20
|
||
# Assuming v5 might be areaid_ft or another high-voltage area
|
||
ptap_hv = tap.and(psdm).and(hvi) # ptap inside hv areas and thkox
|
||
ndiff_hv = diff.and(nsdm).and(hvi).not_interacting(ptap_hv) # ndiff inside hv areas and thkox
|
||
|
||
# Check spacing when ndiff is non-abutting ptap
|
||
gap_violations = ndiff_hv.separation(ndiff_hv, 1.07, euclidian).polygons
|
||
gap_violations.interacting(ptap_hv).output(
|
||
"MR_hv.SP.10",
|
||
"MR_hv.SP.10 : ndiff inside (v12, v20 and thkox) minimum spacing when non-abutting ptap : 1.07um"
|
||
)
|
||
log("END: 80/20 (thkox)")
|
||
|
||
# poly
|
||
log("START: 66/20 (poly)")
|
||
poly.width(0.15, euclidian).output(
|
||
"MR_poly.WID.2",
|
||
"MR_poly.WID.2 : min. poly width : 0.15um"
|
||
)
|
||
poly
|
||
.not(areaid_ce)
|
||
.space(0.21, euclidian)
|
||
.output("MR_poly.SP.1", "MR_poly.SP.1 : min. poly spacing : 0.21um")
|
||
core_poly_gap = poly.inside(areaid_ce).drc(width(projection) <= 0.15).polygons
|
||
poly
|
||
.interacting(core_poly_gap)
|
||
.isolated(0.16, projection)
|
||
.output("MR_poly.SP.4", "MR_poly.SP.4 : min. poly spacing inside areaid:core : 0.16um")
|
||
|
||
# Additional MR poly resistor rules
|
||
# MR_poly.LEN.2: poly:res minimum length for rpoly_hp and rpoly_hp2K resistors ≥ 0.66
|
||
poly_rs.drc(bbox_max < 0.66).output(
|
||
"MR_poly.LEN.2",
|
||
"MR_poly.LEN.2 : poly:res minimum length for rpoly_hp and rpoly_hp2K resistors : 0.66um"
|
||
)
|
||
|
||
# MR_poly.WID.3: poly:res minimum width ≥ 0.33
|
||
poly_rs.width(0.33).output(
|
||
"MR_poly.WID.3",
|
||
"MR_poly.WID.3 : poly:res minimum width : 0.33um"
|
||
)
|
||
|
||
log("END: 66/20 (poly)")
|
||
|
||
# ldntm
|
||
log("START: 80/20 (ldntm)")
|
||
ldntm_core = ldntm.and(areaid_ce)
|
||
ldntm_core.width(0.7, euclidian).output(
|
||
"ldntm.1",
|
||
"ldntm.1 : min. ldntm width inside areaid:core : 0.7um"
|
||
)
|
||
ldntm_core.space(0.7, euclidian).output(
|
||
"ldntm.2",
|
||
"ldntm.2 : min. ldntm spacing inside areaid:core : 0.7um"
|
||
)
|
||
log("END: 80/20 (ldntm)")
|
||
|
||
# rpm
|
||
log("START: 86/20 (rpm)")
|
||
rpm.width(1.27, euclidian).output(
|
||
"MR_rpm.WID.1",
|
||
"MR_rpm.WID.1 : min. rpm width : 1.27um"
|
||
)
|
||
rpm.space(0.84, euclidian).output(
|
||
"MR_rpm.SP.1",
|
||
"MR_rpm.SP.1 : min. rpm spacing : 0.84um"
|
||
)
|
||
log("END: 86/20 (rpm)")
|
||
|
||
# rdl
|
||
log("START: 74/20 (rdl)")
|
||
rdl.width(10.um, euclidian).output("MR_rdl.WID.1", "MR_rdl.WID.1 : min. rdl width : 10um")
|
||
rdl.space(10.um, euclidian).output("MR_rdl.SP.1", "MR_rdl.SP.1 : min. rdl spacing : 10um")
|
||
rdl.output("MR_rdl.CON.1", "MR_rdl.CON.1 : use of rdl layer is prohibited")
|
||
log("END: 74/20 (rdl)")
|
||
|
||
# urpm
|
||
log("START: 79/20 (urpm)")
|
||
urpm.width(1.27, euclidian).output(
|
||
"MR_urpm.WID.1",
|
||
"MR_urpm.WID.1 : min. rpm width : 1.27um"
|
||
)
|
||
urpm.space(0.84, euclidian).output(
|
||
"MR_urpm.SP.1",
|
||
"MR_urpm.SP.1 : min. rpm spacing : 0.84um"
|
||
)
|
||
log("END: 79/20 (urpm)")
|
||
|
||
# npc
|
||
log("START: 95/20 (npc)")
|
||
npc.width(0.27, euclidian).output("MR_npc.WID.1", "MR_npc.WID.1 : min. npc width : 0.27um")
|
||
npc.space(0.27, euclidian).output(
|
||
"npc.2",
|
||
"npc.2 : min. npc spacing, should be manually merged if less than : 0.27um"
|
||
)
|
||
log("END: 95/20 (npc)")
|
||
|
||
# nsdm
|
||
log("START: 93/44 (nsdm)")
|
||
if SRAM_EXCLUDE
|
||
not_sram_nsdm
|
||
.not(areaid_ce)
|
||
.space(0.38, euclidian)
|
||
.output("MR_nsdm.SP.1", "MR_nsdm.SP.1 : min. nsdm spacing in periphery : 0.38um")
|
||
not_sram_nsdm
|
||
.outside(areaid_ce)
|
||
.width(0.38, euclidian)
|
||
.output("MR_nsdm.WID.2", "MR_nsdm.WID.2 : min. nsdm width in periphery : 0.38um")
|
||
not_sram_nsdm_width =
|
||
not_sram_nsdm
|
||
.width(0.38, euclidian)
|
||
.polygons
|
||
.interacting(areaid_ce_merged)
|
||
not_sram_nsdm_width.interacting(
|
||
not_sram_nsdm_width.edges.outside_part(areaid_ce_merged)
|
||
).output("MR_nsdm.WID.1", "MR_nsdm.WID.1 : min. diff width across areaid:ce : 0.38um")
|
||
nsdm_space =
|
||
not_sram_nsdm
|
||
.space(0.38, euclidian)
|
||
.polygons
|
||
.interacting(areaid_ce_merged)
|
||
nsdm_space.interacting(
|
||
nsdm_space.edges.outside_part(areaid_ce_merged)
|
||
).output("MR_nsdm.SP.2", "MR_nsdm.SP.2 : min. spacing across areaid:ce : 0.38um")
|
||
not_sram_nsdm
|
||
.inside(areaid_ce)
|
||
.width(0.29, euclidian)
|
||
.output("MR_nsdm.WID.3", "MR_nsdm.WID.3 : min. nsdm width in areaid:ce : 0.29um")
|
||
not_sram_nsdm
|
||
.inside(areaid_ce)
|
||
.space(0.29, euclidian)
|
||
.output("MR_nsdm.SP.3", "MR_nsdm.SP.3 : min. nsdm spacing in areaid:ce : 0.29um")
|
||
not_sram_nsdm
|
||
.inside(areaid_ce)
|
||
.space(0.38, projection)
|
||
.with_internal_angle(0)
|
||
.output(
|
||
"nsdm.6a",
|
||
"nsdm.6a : min. nsdm spacing on parallel edges in areaid:ce : 0.38um"
|
||
)
|
||
else
|
||
nsdm
|
||
.not(areaid_ce)
|
||
.space(0.38, euclidian)
|
||
.output("MR_nsdm.SP.1", "MR_nsdm.SP.1 : min. nsdm spacing in periphery : 0.38um")
|
||
nsdm
|
||
.outside(areaid_ce)
|
||
.width(0.38, euclidian)
|
||
.output("MR_nsdm.WID.2", "MR_nsdm.WID.2 : min. nsdm width in periphery : 0.38um")
|
||
nsdm_width =
|
||
nsdm.width(0.38, euclidian).polygons.interacting(areaid_ce_merged)
|
||
nsdm_width.interacting(
|
||
nsdm_width.edges.outside_part(areaid_ce_merged)
|
||
).output("MR_nsdm.WID.1", "MR_nsdm.WID.1 : min. diff width across areaid:ce : 0.38um")
|
||
nsdm_space =
|
||
nsdm.space(0.38, euclidian).polygons.interacting(areaid_ce_merged)
|
||
nsdm_space.interacting(
|
||
nsdm_space.edges.outside_part(areaid_ce_merged)
|
||
).output("MR_nsdm.SP.2", "MR_nsdm.SP.2 : min. spacing across areaid:ce : 0.38um")
|
||
nsdm
|
||
.inside(areaid_ce)
|
||
.width(0.29, euclidian)
|
||
.output("MR_nsdm.WID.3", "MR_nsdm.WID.3 : min. nsdm width in areaid:ce : 0.29um")
|
||
nsdm
|
||
.inside(areaid_ce)
|
||
.space(0.29, euclidian)
|
||
.output("MR_nsdm.SP.3", "MR_nsdm.SP.3 : min. nsdm spacing in areaid:ce : 0.29um")
|
||
nsdm
|
||
.inside(areaid_ce)
|
||
.space(0.38, projection)
|
||
.with_internal_angle(0)
|
||
.output(
|
||
"nsdm.6a",
|
||
"nsdm.6a : min. nsdm spacing on parallel edges in areaid:ce : 0.38um"
|
||
)
|
||
end # SRAM_EXCLUDE
|
||
|
||
log("END: 93/44 (nsdm)")
|
||
|
||
# psdm
|
||
log("START: 94/20 (psdm)")
|
||
if SRAM_EXCLUDE
|
||
not_sram_psdm
|
||
.not(areaid_ce)
|
||
.space(0.38, euclidian)
|
||
.output("MR_psdm.SP.1", "MR_psdm.SP.1 : min. psdm spacing in periphery : 0.38um")
|
||
not_sram_psdm
|
||
.outside(areaid_ce)
|
||
.width(0.38, euclidian)
|
||
.output("MR_psdm.WID.2", "MR_psdm.WID.2 : min. psdm width in periphery : 0.38um")
|
||
not_sram_psdm_width =
|
||
not_sram_psdm
|
||
.width(0.38, euclidian)
|
||
.polygons
|
||
.interacting(areaid_ce_merged)
|
||
not_sram_psdm_width.interacting(
|
||
not_sram_psdm_width.edges.outside_part(areaid_ce_merged)
|
||
).output("MR_psdm.WID.1", "MR_psdm.WID.1 : min. diff width across areaid:ce : 0.38um")
|
||
psdm_space =
|
||
not_sram_psdm
|
||
.space(0.38, euclidian)
|
||
.polygons
|
||
.interacting(areaid_ce_merged)
|
||
psdm_space.interacting(
|
||
psdm_space.edges.outside_part(areaid_ce_merged)
|
||
).output("MR_psdm.SP.2", "MR_psdm.SP.2 : min. spacing across areaid:ce : 0.38um")
|
||
not_sram_psdm
|
||
.inside(areaid_ce)
|
||
.width(0.29, euclidian)
|
||
.output("MR_psdm.WID.3", "MR_psdm.WID.3 : min. psdm width in areaid:ce : 0.29um")
|
||
not_sram_psdm
|
||
.inside(areaid_ce)
|
||
.space(0.29, euclidian)
|
||
.output("MR_psdm.SP.3", "MR_psdm.SP.3 : min. psdm spacing in areaid:ce : 0.29um")
|
||
not_sram_psdm
|
||
.inside(areaid_ce)
|
||
.space(0.38, projection)
|
||
.with_internal_angle(0)
|
||
.output(
|
||
"psdm.6a",
|
||
"psdm.6a : min. psdm spacing on parallel edges in areaid:ce : 0.38um"
|
||
)
|
||
not_sram_psdm
|
||
.and(not_sram_nsdm)
|
||
.and(diff)
|
||
.output(
|
||
"nsdm_psdm_overlap",
|
||
"nsdm_psdm_overlap : nsdm overlaps psdm over active"
|
||
)
|
||
else
|
||
psdm
|
||
.not(areaid_ce)
|
||
.space(0.38, euclidian)
|
||
.output("MR_psdm.SP.1", "MR_psdm.SP.1 : min. psdm spacing in periphery : 0.38um")
|
||
psdm
|
||
.outside(areaid_ce)
|
||
.width(0.38, euclidian)
|
||
.output("MR_psdm.WID.2", "MR_psdm.WID.2 : min. psdm width in periphery : 0.38um")
|
||
psdm_width =
|
||
psdm.width(0.38, euclidian).polygons.interacting(areaid_ce_merged)
|
||
psdm_width.interacting(
|
||
psdm_width.edges.outside_part(areaid_ce_merged)
|
||
).output("MR_psdm.WID.1", "MR_psdm.WID.1 : min. diff width across areaid:ce : 0.38um")
|
||
psdm_space =
|
||
psdm.space(0.38, euclidian).polygons.interacting(areaid_ce_merged)
|
||
psdm_space.interacting(
|
||
psdm_space.edges.outside_part(areaid_ce_merged)
|
||
).output("MR_psdm.SP.2", "MR_psdm.SP.2 : min. spacing across areaid:ce : 0.38um")
|
||
psdm
|
||
.inside(areaid_ce)
|
||
.width(0.29, euclidian)
|
||
.output("MR_psdm.WID.3", "MR_psdm.WID.3 : min. psdm width in areaid:ce : 0.29um")
|
||
psdm
|
||
.inside(areaid_ce)
|
||
.space(0.29, euclidian)
|
||
.output("MR_psdm.SP.3", "MR_psdm.SP.3 : min. psdm spacing in areaid:ce : 0.29um")
|
||
psdm
|
||
.inside(areaid_ce)
|
||
.space(0.38, projection)
|
||
.with_internal_angle(0)
|
||
.output(
|
||
"psdm.6a",
|
||
"psdm.6a : min. psdm spacing on parallel edges in areaid:ce : 0.38um"
|
||
)
|
||
psdm
|
||
.and(nsdm)
|
||
.and(diff)
|
||
.output(
|
||
"nsdm_psdm_overlap",
|
||
"nsdm_psdm_overlap : nsdm overlaps psdm over active"
|
||
)
|
||
end # SRAM_EXCLUDE
|
||
|
||
log("END: 94/20 (psdm)")
|
||
|
||
# licon
|
||
log("START: 66/44 (licon)")
|
||
polyi = poly_fill.and(poly)
|
||
poly_licon = (polyi.and(licon)).and(areaid_ce)
|
||
licon_peri = licon.outside(areaid_ce)
|
||
|
||
licon_squares = licon.drc((squares) & (bbox_min == 0.17).polygons)
|
||
licon_not_squares = licon.not(licon_squares)
|
||
licon_bars = licon.drc((bbox_max == 2.0) & (bbox_min == 0.19).polygons)
|
||
|
||
#if INFO
|
||
# licon_bars.output(
|
||
# "licon_bar",
|
||
# "licon bar : Info only"
|
||
# )
|
||
#end
|
||
|
||
if SEAL
|
||
ringLICON = licon.drc(with_holes > 0)
|
||
rectLICON = licon.not(ringLICON)
|
||
else
|
||
rectLICON = licon
|
||
end
|
||
rectLICON.non_rectangles.output(
|
||
"licon.1_c",
|
||
"licon.1_c : licon should be rectangle"
|
||
)
|
||
prec_resistor = (rpm | urpm) & psdm & (poly.interacting(poly_rs))
|
||
licon_peri.not(prec_resistor).space(0.17, euclidian).output(
|
||
"MR_licon.SP.1",
|
||
"MR_licon.SP.1: min. licon spacing in periphery : 0.17um"
|
||
)
|
||
licon_not_prec_resistor = licon.not(prec_resistor)
|
||
licon_not_prec_resistor.drc(length != 0.17).output(
|
||
"licon.1",
|
||
"licon.1: min/max. licon length : 0.17um"
|
||
)
|
||
licon_peri
|
||
.and(difftap)
|
||
.separation(npc, 0.09, euclidian)
|
||
.output(
|
||
"licon.13",
|
||
"licon.13: min. licon on diff spacing to npc in periphery : 0.09um"
|
||
)
|
||
licon_peri
|
||
.and(difftap)
|
||
.and(npc)
|
||
.output(
|
||
"licon.13_a",
|
||
"licon.13_a: licon on diff in periphery can't overlap npc"
|
||
)
|
||
licon
|
||
.and(poly)
|
||
.and(difftap)
|
||
.output(
|
||
"licon.17",
|
||
"licon.17 : Licons may not overlap both poly and (diff or tap)"
|
||
)
|
||
xfom = difftap.not(poly)
|
||
licon1ToXfom = licon.interacting(licon.and(xfom))
|
||
polyLicon1 =
|
||
(licon.not(licon1ToXfom)).interacting((licon.not(licon1ToXfom)).and(poly))
|
||
polyLicon1_CORE = polyLicon1.and(areaid_ce)
|
||
npc.enclosing(polyLicon1_CORE, 0.045, euclidian).output(
|
||
"licon.6",
|
||
"licon.6 : npc min enclosure of poly_licon inside areaid:core : 0.045um"
|
||
)
|
||
polyLicon1_CORE.not(npc).output(
|
||
"licon.6",
|
||
"licon.6 : npc min enclosure of poly_licon inside areaid:core : 0.045um"
|
||
)
|
||
|
||
# Additional MR licon bar rules
|
||
# MR_licon.ANG.1: licon outside areaid:seal must be orthogonal
|
||
licon_outside_seal = licon.outside(areaid_sl)
|
||
licon_outside_seal.with_angle(0..90).output(
|
||
"MR_licon.ANG.1",
|
||
"MR_licon.ANG.1 : licon outside areaid:seal must be orthogonal"
|
||
)
|
||
|
||
# MR_licon.CON.13: licon bar outside prec_resistor is prohibited
|
||
licon_bars_outside_prec_resistor = licon_bars.outside(prec_resistor)
|
||
licon_bars_outside_prec_resistor.output(
|
||
"MR_licon.CON.13",
|
||
"MR_licon.CON.13 : licon bar outside prec_resistor is prohibited"
|
||
)
|
||
|
||
licon_squares_rpm_urpm = licon_squares.interacting(rpm | urpm)
|
||
licon_squares_rpm_urpm.output(
|
||
"MR_licon.CON.xx",
|
||
"MR_licon.CON.xx : licon square interacting with rpm/urpm is prohibited"
|
||
)
|
||
|
||
# MR_licon.SP.2: licon bar end to end minimum spacing ≥ 0.35
|
||
licon_bars.drc(width == 2.00).space(0.35, euclidian).output(
|
||
"MR_licon.SP.2",
|
||
"MR_licon.SP.2 : licon bar end to end minimum spacing : 0.35um"
|
||
)
|
||
|
||
# MR_licon.SP.3: licon bar side to side minimum spacing ≥ 0.51
|
||
# This is handled by the general spacing rule above, but we can add specific check
|
||
licon_bars.drc(width == 0.19).space(0.51, euclidian).output(
|
||
"MR_licon.SP.3",
|
||
"MR_licon.SP.3 : licon bar side to side minimum spacing : 0.51um"
|
||
)
|
||
|
||
# MR_licon.SP.4: licon bar minimum space to licon square ≥ 0.51
|
||
licon_bars.separation(licon_squares, 0.51, euclidian).output(
|
||
"MR_licon.SP.4",
|
||
"MR_licon.SP.4 : licon bar minimum space to licon square : 0.51um"
|
||
)
|
||
|
||
# MRW_licon.SP.16: 0.19 width bar licons (post-maskgen) bar licon spacing < 0.51
|
||
#licon_bars.space(0.51, euclidian).output(
|
||
# "MRW_licon.SP.16",
|
||
# "MRW_licon.SP.16 : 0.19 width bar licons (post-maskgen) bar licon spacing < 0.51"
|
||
#)
|
||
|
||
# MR_licon.SP.5: tap_licon minimum space to tap edge abutting diff ≥ 0.06
|
||
tap_licon = licon.and(tap)
|
||
tap_licon.separation(diff, 0.06, euclidian).output(
|
||
"MR_licon.SP.5",
|
||
"MR_licon.SP.5 : tap_licon minimum space to tap edge abutting diff : 0.06um"
|
||
)
|
||
|
||
# MR_licon.SP.6: poly_licon minimum space to psdm (overlap prohibited) ≥ 0.11
|
||
poly_licon_not_prec_resistor = licon_not_prec_resistor.and(poly)
|
||
poly_licon_not_prec_resistor.separation(psdm, 0.11, euclidian).output(
|
||
"MR_licon.SP.6",
|
||
"MR_licon.SP.6 : poly_licon minimum space to psdm (overlap prohibited) : 0.11um"
|
||
)
|
||
poly_licon_not_prec_resistor.interacting(psdm).output(
|
||
"MR_licon.SP.6_overlap",
|
||
"MR_licon.SP.6 : licon and psdm must not overlap"
|
||
)
|
||
|
||
# MR_licon.SP.8: licon on diff minimum space to gate outside areaid:standardc (overlap prohibited) ≥ 0.055
|
||
licon_on_diff = licon.and(difftap)
|
||
licon_on_diff_outside_stdc = licon_on_diff.outside(areaid_sc)
|
||
licon_on_diff_outside_stdc.separation(poly, 0.055, euclidian).output(
|
||
"MR_licon.SP.8",
|
||
"MR_licon.SP.8 : licon on diff minimum space to gate outside areaid:standardc (overlap prohibited) : 0.055um"
|
||
)
|
||
|
||
# MR_licon.WID.1: licon (non-bar) minimum/maximum width == 0.17
|
||
licon_non_bar = licon.drc((length <= 0.17).polygons)
|
||
licon_non_bar.drc(width != 0.17).output(
|
||
"MR_licon.WID.1",
|
||
"MR_licon.WID.1 : licon (non-bar) minimum/maximum width : 0.17um"
|
||
)
|
||
|
||
# MR_licon.WID.2: licon bar exact size: 0.19 µm × 2.0 µm
|
||
licon_bars.drc(width(euclidian) != 0.19).output(
|
||
"MR_licon.WID.2",
|
||
"MR_licon.WID.2 : licon bar exact width : 0.19um"
|
||
)
|
||
licon_bars.drc(bbox_max != 2.0).output(
|
||
"MR_licon.WID.2",
|
||
"MR_licon.WID.2 : licon bar exact length : 2.0um"
|
||
)
|
||
|
||
log("END: 66/44 (licon)")
|
||
|
||
#nsm
|
||
log("START: 61/20 (nsm)")
|
||
nsm.space(4.0, euclidian).output("MR_nsm.SP.1", "MR_nsm.SP.1 : nsm min. spacing : 4.0um")
|
||
nsm.width(3.0, euclidian).output("MR_nsm.WID.1", "MR_nsm.WID.1 : nsm min. width : 3.0um")
|
||
log("END: 61/20 (nsm)")
|
||
|
||
# CAPM
|
||
log("START: 89/44 (capm)")
|
||
capm.width(1.0, euclidian).output(
|
||
"MR_capm.WID.1",
|
||
"MR_capm.WID.1 : min. capm width : 1.0um"
|
||
)
|
||
capm.space(0.84, euclidian).output(
|
||
"MR_capm.SP.1",
|
||
"MR_capm.SP.1 : min. capm spacing : 0.84um"
|
||
)
|
||
m3_bot_plate = ((capm.and(m3)).sized(0.14))
|
||
m3_bot_plate
|
||
.isolated(1.2, euclidian)
|
||
.polygons
|
||
.not(m3)
|
||
.output("MR_capm.SP.2", "MR_capm.SP.2 : min. spacing between met3_bot_plate : 1.2um")
|
||
(m3.not_interacting(m3_bot_plate)).separation(
|
||
m3_bot_plate,
|
||
1.2,
|
||
euclidian
|
||
).output(
|
||
"MR_capm.SP.2",
|
||
"MR_capm.SP.2 : min. spacing between met3_bot_plate and met3 : 1.2um"
|
||
)
|
||
# capm.and(m3).enclosing(m3, 0.14, euclidian).output("capm.3", "capm.3 : min. capm and m3 enclosure of m3 : 0.14um")
|
||
m3.enclosing(capm, 0.14, euclidian).output(
|
||
"MR_capm.ENC.1",
|
||
"MR_capm.ENC.1 : min. m3 enclosure of capm : 0.14um"
|
||
)
|
||
capm.enclosing(via3, 0.14, euclidian).output(
|
||
"MR_capm.ENC.2",
|
||
"MR_capm.ENC.2 : min. capm enclosure of via3 : 0.14um"
|
||
)
|
||
capm.separation(via3, 0.14, euclidian).output(
|
||
"MR_capm.SP.3",
|
||
"MR_capm.SP.3 : min. capm spacing to via3 : 0.14um"
|
||
)
|
||
(m3.not_interacting(capm)).separation(capm, 0.5, euclidian).output(
|
||
"MR_capm.SP.2",
|
||
"MR_capm.SP.2 : Min spacing of capm and met3 not overlapping capm : 0.5um"
|
||
)
|
||
log("END: 89/44 (capm)")
|
||
|
||
# CAP2M
|
||
log("START: 97/44 (cap2m)")
|
||
cap2m.width(1.0, euclidian).output(
|
||
"MR_cap2m.WID.1",
|
||
"MR_cap2m.WID.1 : min. cap2m width : 1.0um"
|
||
)
|
||
cap2m.space(0.84, euclidian).output(
|
||
"MR_cap2m.SP.1",
|
||
"MR_cap2m.SP.1 : min. cap2m spacing : 0.84um"
|
||
)
|
||
m4
|
||
.interacting(cap2m)
|
||
.isolated(1.2, euclidian)
|
||
.output("MR_cap2m.SP.2", "MR_cap2m.SP.2 : min. cap2m spacing : 1.2um")
|
||
(m4.interacting(cap2m)).isolated(1.2, euclidian).output(
|
||
"MR_cap2m.SP.2",
|
||
"MR_cap2m.SP.2 : min. spacing of m4_bot_plate : 1.2um"
|
||
)
|
||
cap2m
|
||
.and(m4)
|
||
.enclosing(m4, 0.14, euclidian)
|
||
.output("MR_cap2m.ENC.1", "MR_cap2m.ENC.1 : min. m4 enclosure of cap2m : 0.14um")
|
||
m4.enclosing(cap2m, 0.14, euclidian).output(
|
||
"MR_cap2m.ENC.1",
|
||
"MR_cap2m.ENC.1 : min. m4 enclosure of cap2m : 0.14um"
|
||
)
|
||
cap2m.enclosing(via4, 0.2, euclidian).output(
|
||
"MR_cap2m.ENC.2",
|
||
"MR_cap2m.ENC.2 : min. cap2m enclosure of via4 : 0.2um"
|
||
)
|
||
cap2m.separation(via4, 0.2, euclidian).output(
|
||
"MR_cap2m.SP.3",
|
||
"MR_cap2m.SP.3 : min. cap2m spacing to via4 : 0.2um"
|
||
)
|
||
(m4.not_interacting(cap2m)).separation(cap2m, 0.5, euclidian).output(
|
||
"MR_cap2m.SP.2",
|
||
"MR_cap2m.SP.2 : Min spacing of cap2m and met4 not overlapping cap2m : 0.5um"
|
||
)
|
||
log("END: 97/44 (cap2m)")
|
||
end #FEOL
|
||
|
||
if BEOL
|
||
log("BEOL section")
|
||
|
||
# li
|
||
log("START: 67/20 (li)")
|
||
li_outside_or_touching_areaidce =
|
||
(li.outside(areaid_ce)).or(
|
||
li.interacting(areaid_ce).not(li.inside(areaid_ce))
|
||
)
|
||
li_outside_or_touching_areaidce.width(0.17, euclidian).output(
|
||
"li.1",
|
||
"li.1 : min. li width outside or crossing areaid:ce : 0.17um"
|
||
)
|
||
li_outside_or_touching_areaidce.space(0.17, euclidian).output(
|
||
"li.3",
|
||
"li.3 : min. li spacing outside or crossing areaid:ce : 0.17um"
|
||
)
|
||
licon_peri = licon.not(areaid_ce)
|
||
li_edges_with_less_enclosure =
|
||
li.enclosing(licon_peri, 0.08, projection).second_edges
|
||
error_corners = li_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu)
|
||
li_interact = licon_peri.interacting(error_corners.polygons(1.dbu))
|
||
li_interact.output(
|
||
"li.5",
|
||
"li.5 : min. li enclosure of licon of 2 adjacent edges : 0.08um"
|
||
)
|
||
linotace = li.not(li.interacting(areaid_ce))
|
||
linotace.with_area(nil, 0.0561).output(
|
||
"li.6",
|
||
"li.6 : min. li area : 0.0561um²"
|
||
)
|
||
li_core = li.and(areaid_ce)
|
||
li_core.space(0.14, euclidian).output(
|
||
"li.7",
|
||
"li.7 : min. li core spacing : 0.14um"
|
||
)
|
||
li_core.width(0.14, euclidian).output(
|
||
"li.8",
|
||
"li.8 : min. li core width : 0.14um"
|
||
)
|
||
log("END: 67/20 (li)")
|
||
|
||
# li res
|
||
log("START: 67/13 (li_res)")
|
||
li_res.width(0.29, euclidian).output(
|
||
"MR_li.WID.4",
|
||
"MR_li.WID.4 : li:res minimum width : 0.29um"
|
||
)
|
||
log("END: 67/13 (li_res)")
|
||
|
||
# ct
|
||
log("START: 67/44 (mcon)")
|
||
mconnotace = mcon.not(areaid_ce)
|
||
if SEAL
|
||
ringMCON = mcon.drc(with_holes > 0)
|
||
rectMCON = mcon.not(ringMCON)
|
||
else
|
||
rectMCON = mcon
|
||
end
|
||
rectMCON_peri = rectMCON.outside(areaid_ce)
|
||
rectMCON.non_rectangles.output(
|
||
"MR_mcon.ANG.1",
|
||
"MR_mcon.ANG.1: mcon must be rectangular"
|
||
)
|
||
# rectMCON_peri.edges.without_length(0.17).output("ct.1_a/b", "ct.1_a/b : minimum/maximum width of mcon : 0.17um")
|
||
rectMCON_peri.drc(width(euclidian) < 0.17).output(
|
||
"MR_mcon.WID.1",
|
||
"MR_mcon.WID.1 : minimum width of mcon : 0.17um"
|
||
)
|
||
rectMCON_peri.drc(length > 0.17).output(
|
||
"MR_mcon.LEN.1",
|
||
"MR_mcon.LEN.1 : maximum length of mcon : 0.17um"
|
||
)
|
||
mcon.space(0.19, euclidian).output(
|
||
"MR_mcon.SP.1",
|
||
"MR_mcon.SP.1 : min. mcon spacing : 0.19um"
|
||
)
|
||
if SEAL
|
||
ringMCON.width(0.17, euclidian).output(
|
||
"MR_mcon.WID.2",
|
||
"MR_mcon.WID.2 : min. width of ring-shaped mcon : 0.17um"
|
||
)
|
||
ringMCON.drc(width(euclidian) >= 0.175).output(
|
||
"MR_mcon.WID.3",
|
||
"MR_mcon.WID.3 : max. width of ring-shaped mcon : 0.175um"
|
||
)
|
||
ringMCON.not(areaid_sl).output(
|
||
"MR_mcon.CON.1",
|
||
"MR_mcon.CON.1: ring-shaped mcon must be enclosed by areaid_sl"
|
||
)
|
||
end
|
||
mconnotace.not(li).output("MR_mcon.CON.10", "MR_mcon.CON.10 : mcon should covered by li")
|
||
log("END: 67/44 (mcon)")
|
||
|
||
# m1
|
||
log("START: 68/20 (m1)")
|
||
m1.width(0.14, euclidian).output("MR_met1.WID.1", "MR_met1.WID.1 : min. m1 width : 0.14um")
|
||
huge_m1 = m1.sized(-1.5).sized(1.5).snap(0.005) & m1
|
||
non_huge_m1 = m1.edges - huge_m1
|
||
huge_m1 = huge_m1.edges.outside_part(m1.merged)
|
||
|
||
non_huge_m1.space(0.14, euclidian).output(
|
||
"MR_met1.SP.1",
|
||
"MR_met1.SP.1 : min. m1 spacing : 0.14um"
|
||
)
|
||
|
||
(
|
||
huge_m1.separation(non_huge_m1, 0.28, euclidian) +
|
||
huge_m1.space(0.28, euclidian)
|
||
).output("MRW_met1.SP.2", "MRW_met1.SP.2 : min. 3um.m1 spacing m1 : 0.28um")
|
||
|
||
#not_in_cell6 = layout(source.cell_obj).select("-s8cell_ee_plus_sseln_a", "-s8cell_ee_plus_sseln_b", "-s8cell_ee_plus_sselp_a", "-s8cell_ee_plus_sselp_b", "-s8fpls_pl8", "-s8fs_cmux4_fm")
|
||
not_in_cell6 =
|
||
layout(source.cell_obj).select(
|
||
"-s8cell_ee_plus_sseln_a",
|
||
"-s8cell_ee_plus_sseln_b",
|
||
"-s8cell_ee_plus_sselp_a",
|
||
"-s8cell_ee_plus_sselp_b",
|
||
"-s8fs_cmux4_fm"
|
||
)
|
||
not_in_cell6_m1 = not_in_cell6.input(m1_wildcard)
|
||
|
||
not_in_cell6_m1.enclosing(mconnotace, 0.03, euclidian).output(
|
||
"MR_met1.ENC.1",
|
||
"MR_met1.ENC.1 : min. m1 enclosure of mcon : 0.03um"
|
||
)
|
||
mconnotace.not(m1).output(
|
||
"MR_met1.CON.1",
|
||
"MR_met1.CON.1 : mcon periphery must be enclosed by m1"
|
||
)
|
||
in_cell6 =
|
||
layout(source.cell_obj).select(
|
||
"-*",
|
||
"+s8cell_ee_plus_sseln_a",
|
||
"+s8cell_ee_plus_sseln_b",
|
||
"+s8cell_ee_plus_sselp_a",
|
||
"+s8cell_ee_plus_sselp_b",
|
||
"+s8fpls_pl8",
|
||
"+s8fs_cmux4_fm"
|
||
)
|
||
in_cell6_m1 = in_cell6.input(m1_wildcard)
|
||
in_cell6_m1.enclosing(mcon, 0.005, euclidian).output(
|
||
"m1.4a",
|
||
"m1.4a : min. m1 enclosure of mcon for specific cells : 0.005um"
|
||
)
|
||
|
||
in_cell6_m1.not(m1).output(
|
||
"m1.4a_a",
|
||
"m1.4a_a : mcon periph must be enclosed by met1 for specific cells"
|
||
)
|
||
|
||
m1.with_area(0..0.083).output("MR_met1.AR.1", "MR_met1.AR.1 : min. m1 area : 0.083um²")
|
||
|
||
m1
|
||
.holes
|
||
.with_area(0..0.14)
|
||
.output("MR_met1.AR.2", "MR_met1.AR.2 : min. m1 with holes area : 0.14um²")
|
||
if FLOATING_MET
|
||
m1.not_interacting(via.or(mcon)).output(
|
||
"m1.x",
|
||
"floating met1, must interact with via1"
|
||
)
|
||
end
|
||
|
||
if backend_flow == AL
|
||
#Could flag false positive, fix would be to add .rectangles for m1
|
||
mconnotace_edges_with_less_enclosure_m1 =
|
||
m1.enclosing(mconnotace, 0.06, projection).second_edges
|
||
error_corners_m1 =
|
||
mconnotace_edges_with_less_enclosure_m1.width(angle_limit(100.0), 1.dbu)
|
||
mconnotace_interact_m1 =
|
||
mconnotace.interacting(error_corners_m1.polygons(1.dbu))
|
||
mconnotace_interact_m1.output(
|
||
"m1.5",
|
||
"m1.5 : min. m1 enclosure of mcon of 2 adjacent edges : 0.06um"
|
||
)
|
||
end
|
||
log("END: 68/20 (m1)")
|
||
|
||
# via
|
||
log("START: 68/44 (via)")
|
||
if backend_flow == AL
|
||
if SEAL
|
||
ringVIA = via.drc(with_holes > 0)
|
||
rectVIA = via.not(ringVIA)
|
||
else
|
||
rectVIA = via
|
||
end
|
||
|
||
via_not_mt = rectVIA.not(areaid_mt)
|
||
|
||
via_not_mt.non_rectangles.output(
|
||
"via.1a",
|
||
"via.1a : via outside of moduleCut should be rectangular"
|
||
)
|
||
via_not_mt.width(0.15, euclidian).output(
|
||
"via.1a_a",
|
||
"via.1a_a : min. width of via outside of moduleCut : 0.15um"
|
||
)
|
||
# via_not_mt.edges.without_length(nil, 0.15 + 1.dbu).output("via.1a_b", "via.1a_b : maximum length of via : 0.15um")
|
||
via_not_mt.drc(length > 0.15).output(
|
||
"via.1a_b",
|
||
"via.1a_b : maximum length of via : 0.15um"
|
||
)
|
||
|
||
via.space(0.17, euclidian).output(
|
||
"via.2",
|
||
"via.2 : min. via spacing : 0.17um"
|
||
)
|
||
|
||
if SEAL
|
||
ringVIA.width(0.2, euclidian).output(
|
||
"via.3",
|
||
"via.3 : min. width of ring-shaped via : 0.2um"
|
||
)
|
||
ringVIA.drc(width(euclidian) >= 0.205).output(
|
||
"via.3_a",
|
||
"via.3_a : max. width of ring-shaped via : 0.205um"
|
||
)
|
||
ringVIA.not(areaid_sl).output(
|
||
"via.3_b",
|
||
"via.3_b: ring-shaped via must be enclosed by areaid_sl"
|
||
)
|
||
end
|
||
|
||
m1
|
||
.edges
|
||
.enclosing(rectVIA.drc(width == 0.15), 0.055, euclidian)
|
||
.output("MR_via1.ENC.1", "MR_via1.ENC.1 : min. m1 enclosure of 0.15um via : 0.055um")
|
||
rectVIA
|
||
.squares
|
||
.drc(width == 0.15)
|
||
.not(m1)
|
||
.output("MR_via1.CON.1", "MR_via1.CON.1 : 0.15um via must be enclosed by met1")
|
||
|
||
via1_edges_with_less_enclosure_m1 =
|
||
m1
|
||
.edges
|
||
.enclosing(rectVIA.drc(width == 0.15), 0.085, projection)
|
||
.second_edges
|
||
error_corners_via1 =
|
||
via1_edges_with_less_enclosure_m1.width(angle_limit(100.0), 1.dbu)
|
||
via2_interact = via.interacting(error_corners_via1.polygons(1.dbu))
|
||
via2_interact.output(
|
||
"via.5a",
|
||
"via.5a : min. m1 enclosure of 0.15um via of 2 adjacent edges : 0.085um"
|
||
)
|
||
end
|
||
log("END: 68/44 (via)")
|
||
|
||
# m2
|
||
log("START: 69/20 (m2)")
|
||
via_inside_periphery = via.and(areaid_ce)
|
||
m2.width(0.14, euclidian).output("MR_met2.WID.1", "MR_met2.WID.1 : min. m2 width : 0.14um")
|
||
|
||
huge_m2 = m2.sized(-1.5).sized(1.5).snap(0.005) & m2
|
||
non_huge_m2 = m2.edges - huge_m2
|
||
huge_m2 = huge_m2.edges.outside_part(m2.merged)
|
||
via_outside_periphery = via.not(areaid_ce)
|
||
|
||
non_huge_m2.space(0.14, euclidian).output(
|
||
"m2.2",
|
||
"m2.2 : min. m2 spacing : 0.14um"
|
||
)
|
||
|
||
(
|
||
huge_m2.separation(non_huge_m2, 0.28, euclidian) +
|
||
huge_m2.space(0.28, euclidian)
|
||
).output("MRW_met2.SP.2", "MRW_met2.SP.2 : min. 3um.m2 spacing m2 : 0.28um")
|
||
|
||
m2.with_area(0..0.0676).output("MR_met2.AR.1", "MR_met2.AR.1 : min. m2 area : 0.0676um²")
|
||
m2
|
||
.holes
|
||
.with_area(0..0.14)
|
||
.output("MR_met2.AR.2", "MR_met2.AR.2 : min. m2 holes area : 0.14um²")
|
||
if FLOATING_MET
|
||
m2.not_interacting(via.or(via2)).output(
|
||
"m2.x",
|
||
"floating met2, must interact with via1 or via2"
|
||
)
|
||
end
|
||
if backend_flow == AL
|
||
m2.enclosing(via_outside_periphery, 0.055, euclidian).output(
|
||
"m2.4",
|
||
"m2.4 : min. m2 enclosure of via : 0.055um"
|
||
)
|
||
via_outside_periphery.not(m2).output(
|
||
"m2.4_a",
|
||
"m2.4_a : via in periphery must be enclosed by met2"
|
||
)
|
||
m2.enclosing(via_inside_periphery, 0.045, euclidian).output(
|
||
"m2.4_b",
|
||
"m2.4_B : min. m2 enclosure of via inside areaid:core : 0.045um"
|
||
)
|
||
via_edges_with_less_enclosure_m2 =
|
||
m2.enclosing(via_outside_periphery, 0.085, projection).second_edges
|
||
error_corners =
|
||
via_edges_with_less_enclosure_m2.width(angle_limit(100.0), 1.dbu)
|
||
via_interact = via.interacting(error_corners.polygons(1.dbu))
|
||
via_interact.output(
|
||
"m2.5",
|
||
"m2.5 : min. m2 enclosure of via of 2 adjacent edges : 0.085um"
|
||
)
|
||
end
|
||
log("END: 69/20 (m2)")
|
||
|
||
# via2
|
||
log("START: 69/44 (via2)")
|
||
if backend_flow == AL
|
||
if SEAL
|
||
ringVIA2 = via2.drc(with_holes > 0)
|
||
rectVIA2 = via2.not(ringVIA2)
|
||
else
|
||
rectVIA2 = via2
|
||
end
|
||
|
||
via2_not_mt = rectVIA2.not(areaid_mt)
|
||
via2_not_mt.non_rectangles.output(
|
||
"via2.1a",
|
||
"via2.1a : via2 outside of moduleCut should be rectangular"
|
||
)
|
||
via2_not_mt.width(0.2, euclidian).output(
|
||
"via2.1a_a",
|
||
"via2.1a_a : min. width of via2 outside of moduleCut : 0.2um"
|
||
)
|
||
via2_not_mt
|
||
.edges
|
||
.without_length(nil, 0.2 + 1.dbu)
|
||
.output("MR_via2.LEN.1", "MR_via2.LEN.1 : maximum length of via2 : 0.2um")
|
||
via2.space(0.2, euclidian).output(
|
||
"via2.2",
|
||
"via2.2 : min. via2 spacing : 0.2um"
|
||
)
|
||
|
||
if SEAL
|
||
ringVIA2.width(0.2, euclidian).output(
|
||
"via2.3",
|
||
"via2.3 : min. width of ring-shaped via2 : 0.2um"
|
||
)
|
||
ringVIA2.drc(width(euclidian) >= 0.205).output(
|
||
"via2.3_a",
|
||
"via2.3_a : max. width of ring-shaped via2 : 0.205um"
|
||
)
|
||
ringVIA2.not(areaid_sl).output(
|
||
"via2.3_b",
|
||
"via2.3_b: ring-shaped via2 must be enclosed by areaid_sl"
|
||
)
|
||
end
|
||
|
||
m2.enclosing(via2, 0.04, euclidian).output(
|
||
"via2.4",
|
||
"via2.4 : min. m2 enclosure of via2 : 0.04um"
|
||
)
|
||
via2.not(m2).output("MR_via2.CON.1", "MR_via2.CON.1 : via must be enclosed by met2")
|
||
|
||
via2_edges_with_less_enclosure =
|
||
m2.enclosing(via2, 0.085, projection).second_edges
|
||
error_corners =
|
||
via2_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu)
|
||
via2_interact = via2.interacting(error_corners.polygons(1.dbu))
|
||
via2_interact.output(
|
||
"via2.5",
|
||
"via2.5 : min. m3 enclosure of via2 of 2 adjacent edges : 0.085um"
|
||
)
|
||
end
|
||
log("END: 69/44 (via2)")
|
||
|
||
# m3
|
||
log("START: 70/20 (m3)")
|
||
m3.width(0.3, euclidian).output("MR_met3.WID.1", "MR_met3.WID.1 : min. m3 width : 0.3um")
|
||
|
||
huge_m3 = m3.sized(-1.5).sized(1.5).snap(0.005) & m3
|
||
non_huge_m3 = m3.edges - huge_m3
|
||
huge_m3 = huge_m3.edges.outside_part(m3.merged)
|
||
|
||
non_huge_m3.space(0.3, euclidian).output(
|
||
"m3.2",
|
||
"m3.2 : min. m3 spacing : 0.3um"
|
||
)
|
||
|
||
(
|
||
huge_m3.separation(non_huge_m3, 0.4, euclidian) +
|
||
huge_m3.space(0.4, euclidian)
|
||
).output("MRW_met3.SP.2", "MRW_met3.SP.2 : min. 3um.m3 spacing m3 : 0.4um")
|
||
if FLOATING_MET
|
||
m3.not_interacting(via2.or(via3)).output(
|
||
"m3.x",
|
||
"floating met3, must interact with via2 or via3"
|
||
)
|
||
end
|
||
if backend_flow == AL
|
||
m3.enclosing(via2, 0.065, euclidian).output(
|
||
"m3.4",
|
||
"m3.4 : min. m3 enclosure of via2 : 0.065um"
|
||
)
|
||
via2.not(m3).output("MR_met3.CON.1", "MR_met3.CON.1 : via2 must be enclosed by met3")
|
||
end
|
||
m3.with_area(0..0.240).output("MR_met3.AR.1", "MR_met3.AR.1 : min. m3 area : 0.240um²")
|
||
m3
|
||
.holes
|
||
.with_area(0..0.2)
|
||
.output("MR_met3.AR.2", "MR_met3.AR.2 : min. m3 holes area : 0.2um²")
|
||
log("END: 70/20 (m3)")
|
||
|
||
# via3
|
||
log("START: 70/44 (via3)")
|
||
if backend_flow == AL
|
||
if SEAL
|
||
ringVIA3 = via3.drc(with_holes > 0)
|
||
rectVIA3 = via3.not(ringVIA3)
|
||
else
|
||
rectVIA3 = via3
|
||
end
|
||
|
||
via3_not_mt = rectVIA3.not(areaid_mt)
|
||
via3_not_mt.non_rectangles.output(
|
||
"via3.1",
|
||
"via3.1 : via3 outside of moduleCut should be rectangular"
|
||
)
|
||
via3_not_mt.width(0.2, euclidian).output(
|
||
"via3.1_a",
|
||
"via3.1_a : min. width of via3 outside of moduleCut : 0.2um"
|
||
)
|
||
via3_not_mt
|
||
.edges
|
||
.without_length(nil, 0.2 + 1.dbu)
|
||
.output("MR_via3.LEN.1", "MR_via3.LEN.1 : maximum length of via3 : 0.2um")
|
||
|
||
via3.space(0.2, euclidian).output(
|
||
"via3.2",
|
||
"via3.2 : min. via3 spacing : 0.2um"
|
||
)
|
||
m3.enclosing(via3, 0.06, euclidian).output(
|
||
"via3.4",
|
||
"via3.4 : min. m3 enclosure of via3 : 0.06um"
|
||
)
|
||
rectVIA3.not(m3).output(
|
||
"via3.4_a",
|
||
"via3.4_a : non-ring via3 must be enclosed by met3"
|
||
)
|
||
|
||
via_edges_with_less_enclosure =
|
||
m3.enclosing(via3, 0.09, projection).second_edges
|
||
error_corners =
|
||
via_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu)
|
||
via3_interact = via3.interacting(error_corners.polygons(1.dbu))
|
||
via3_interact.output(
|
||
"via3.5",
|
||
"via3.5 : min. m3 enclosure of via3 of 2 adjacent edges : 0.09um"
|
||
)
|
||
end
|
||
log("END: 70/44 (via3)")
|
||
|
||
# m4
|
||
log("START: 71/20 (m4)")
|
||
m4.width(0.3, euclidian).output("MR_met4.WID.1", "MR_met4.WID.1 : min. m4 width : 0.3um")
|
||
|
||
huge_m4 = m4.sized(-1.5).sized(1.5).snap(0.005) & m4
|
||
non_huge_m4 = m4.edges - huge_m4
|
||
huge_m4 = huge_m4.edges.outside_part(m4.merged)
|
||
|
||
non_huge_m4.space(0.3, euclidian).output(
|
||
"m4.2",
|
||
"m4.2 : min. m4 spacing : 0.3um"
|
||
)
|
||
|
||
m4.with_area(0..0.240).output("MR_met4.AR.1", "MR_met4.AR.1 : min. m4 area : 0.240um²")
|
||
|
||
(
|
||
huge_m4.separation(non_huge_m4, 0.4, euclidian) +
|
||
huge_m4.space(0.4, euclidian)
|
||
).output("MRW_met4.SP.2", "MRW_met4.SP.2 : min. 3um.m4 spacing m4 : 0.4um")
|
||
if FLOATING_MET
|
||
m4.not_interacting(via3.or(via4)).output(
|
||
"m4.x",
|
||
"floating met3, must interact with via3 or via4"
|
||
)
|
||
end
|
||
if backend_flow == AL
|
||
m4.enclosing(via3, 0.065, euclidian).output(
|
||
"m4.3",
|
||
"m4.3 : min. m4 enclosure of via3 : 0.065um"
|
||
)
|
||
via3.not(m4).output("MR_met4.CON.1", "MR_met4.CON.1 : via3 must be enclosed by met4")
|
||
end
|
||
m4
|
||
.holes
|
||
.with_area(0..0.2)
|
||
.output("MR_met4.AR.2", "MR_met4.AR.2 : min. m4 holes area : 0.2um²")
|
||
log("END: 71/20 (m4)")
|
||
|
||
# via4
|
||
log("START: 71/44 (via4)")
|
||
if SEAL
|
||
ringVIA4 = via4.drc(with_holes > 0)
|
||
rectVIA4 = via4.not(ringVIA4)
|
||
else
|
||
rectVIA4 = via4
|
||
end
|
||
|
||
via4_not_mt = rectVIA4.not(areaid_mt)
|
||
via4_not_mt.non_rectangles.output(
|
||
"via4.1",
|
||
"via4.1 : via4 outside of moduleCut should be rectangular"
|
||
)
|
||
rectVIA4.width(0.8, euclidian).output(
|
||
"via4.1_a",
|
||
"via4.1_a : min. width of via4 outside of moduleCut : 0.8um"
|
||
)
|
||
rectVIA4.drc(length > 0.8).output(
|
||
"via4.1_b",
|
||
"via4.1_b : maximum length of via4 : 0.8um"
|
||
)
|
||
|
||
via4
|
||
.space(0.8, euclidian)
|
||
.polygons
|
||
.output("MR_via4.SP.1", "MR_via4.SP.1 : min. via4 spacing : 0.8um")
|
||
|
||
if SEAL
|
||
ringVIA4.width(0.8, euclidian).output(
|
||
"via4.3",
|
||
"via4.3 : min. width of ring-shaped via4 : 0.8um"
|
||
)
|
||
ringVIA4.drc(width(euclidian) >= 0.805).output(
|
||
"via4.3_a",
|
||
"via4.3_a : max. width of ring-shaped via4 : 0.805um"
|
||
)
|
||
ringVIA4.not(areaid_sl).output(
|
||
"via4.3_b",
|
||
"via4.3_b: ring-shaped via4 must be enclosed by areaid_sl"
|
||
)
|
||
end
|
||
|
||
m4.enclosing(via4, 0.19, euclidian).output(
|
||
"via4.4",
|
||
"via4.4 : min. m4 enclosure of via4 : 0.19um"
|
||
)
|
||
rectVIA4.not(m4).output("MR_via4.CON.1", "MR_via4.CON.1 : m4 must enclose all via4")
|
||
log("END: 71/44 (via4)")
|
||
|
||
# m5
|
||
log("START: 72/20 (m5)")
|
||
m5.width(1.6, euclidian).output("MR_met5.WID.1", "MR_met5.WID.1 : min. m5 width : 1.6um")
|
||
|
||
m5.space(1.6, euclidian).output("MR_met5.SP.1", "MR_met5.SP.1 : min. m5 spacing : 1.6um")
|
||
|
||
m5.enclosing(via4, 0.31, euclidian).output(
|
||
"m5.3",
|
||
"m5.3 : min. m5 enclosure of via4 : 0.31um"
|
||
)
|
||
via4.not(m5).output("MR_met5.CON.1", "MR_met5.CON.1 : via must be enclosed by m5")
|
||
if FLOATING_MET
|
||
m5.not_interacting(via4).output(
|
||
"m5.x",
|
||
"floating met5, must interact with via4"
|
||
)
|
||
end
|
||
m5.with_area(0..4.0).output("MR_met5.AR.1", "MR_met5.AR.1 : min. m5 area : 4.0um²")
|
||
m5
|
||
.holes
|
||
.with_area(0..0.14)
|
||
.output("MR_met5.AR.2", "MR_met5.AR.2 : min. m5 holes area : 0.14um²")
|
||
log("END: 72/20 (m5)")
|
||
|
||
# pad
|
||
log("START: 76/20 (pad)")
|
||
pad.space(1.27, euclidian).output(
|
||
"pad.2",
|
||
"pad.2 : min. pad spacing : 1.27um"
|
||
)
|
||
log("END: 76/20 (pad)")
|
||
log("START: 81/10 (moduleCut)")
|
||
areaid_mt.output(
|
||
"moduleCut.1",
|
||
"moduleCut.1 : moduleCut layer is for SkyWater use only"
|
||
)
|
||
log("END: 81/10 (moduleCut)")
|
||
#areaid_mt = polygons(81, 10)
|
||
end #BEOL
|
||
|
||
if FEOL
|
||
log("FEOL section")
|
||
|
||
# hvi
|
||
log("START: 75/20 (hvi)")
|
||
hvi_peri = hvi.not(areaid_ce)
|
||
hvi_peri.width(0.6, euclidian).output(
|
||
"hvi.1",
|
||
"hvi.1 : min. hvi width : 0.6um"
|
||
)
|
||
hvi_peri.space(0.7, euclidian).output(
|
||
"hvi.2a",
|
||
"hvi.2a : min. hvi spacing : 0.7um"
|
||
)
|
||
log("END: 75/20 (hvi)")
|
||
|
||
# hvntm
|
||
log("START: 125/20 (hvntm)")
|
||
hvntm_peri = hvntm.not(areaid_ce)
|
||
hvntm_peri.width(0.7, euclidian).output(
|
||
"hvntm.1",
|
||
"hvntm.1 : min. hvntm width : 0.7um"
|
||
)
|
||
hvntm_peri.space(0.7, euclidian).output(
|
||
"hvntm.2",
|
||
"hvntm.2 : min. hvntm spacing : 0.7um"
|
||
)
|
||
log("END: 125/20 (hvntm)")
|
||
end #FEOL
|
||
|
||
if OFFGRID
|
||
log("OFFGRID-ANGLES section")
|
||
|
||
dnwell.ongrid(0.005).output(
|
||
"dnwell_OFFGRID",
|
||
"x.1b : OFFGRID vertex on dnwell"
|
||
)
|
||
dnwell.with_angle(0..45).output(
|
||
"dnwell_angle",
|
||
"x.3a : non 45 degree angle dnwell"
|
||
)
|
||
nwell.ongrid(0.005).output("nwell_OFFGRID", "x.1b : OFFGRID vertex on nwell")
|
||
nwell.with_angle(0..45).output(
|
||
"nwell_angle",
|
||
"x.3a : non 45 degree angle nwell"
|
||
)
|
||
pwbm.ongrid(0.005).output("pwbm_OFFGRID", "x.1b : OFFGRID vertex on pwbm")
|
||
pwbm.with_angle(0..45).output("pwbm_angle", "x.3a : non 45 degree angle pwbm")
|
||
pwde.ongrid(0.005).output("pwde_OFFGRID", "x.1b : OFFGRID vertex on pwde")
|
||
pwde.with_angle(0..45).output("pwde_angle", "x.3a : non 45 degree angle pwde")
|
||
hvtp.ongrid(0.005).output("hvtp_OFFGRID", "x.1b : OFFGRID vertex on hvtp")
|
||
hvtp.with_angle(0..45).output("hvtp_angle", "x.3a : non 45 degree angle hvtp")
|
||
hvtr.ongrid(0.005).output("hvtr_OFFGRID", "x.1b : OFFGRID vertex on hvtr")
|
||
hvtr.with_angle(0..45).output("hvtr_angle", "x.3a : non 45 degree angle hvtr")
|
||
lvtn.ongrid(0.005).output("lvtn_OFFGRID", "x.1b : OFFGRID vertex on lvtn")
|
||
lvtn.with_angle(0..45).output("lvtn_angle", "x.3a : non 45 degree angle lvtn")
|
||
ncm.ongrid(0.005).output("ncm_OFFGRID", "x.1b : OFFGRID vertex on ncm")
|
||
ncm.with_angle(0..45).output("ncm_angle", "x.3a : non 45 degree angle ncm")
|
||
diff.ongrid(0.005).output("diff_OFFGRID", "x.1b : OFFGRID vertex on diff")
|
||
tap.ongrid(0.005).output("tap_OFFGRID", "x.1b : OFFGRID vertex on tap")
|
||
diff
|
||
.not(areaid_en.and(uhvi))
|
||
.with_angle(0..90)
|
||
.output("diff_angle", "x.2 : non 90 degree angle diff")
|
||
diff
|
||
.and(areaid_en.and(uhvi))
|
||
.with_angle(0..45)
|
||
.output("diff_angle", "x.2c : non 45 degree angle diff")
|
||
tap
|
||
.not(areaid_en.and(uhvi))
|
||
.with_angle(0..90)
|
||
.output("tap_angle", "x.2 : non 90 degree angle tap")
|
||
tap
|
||
.and(areaid_en.and(uhvi))
|
||
.with_angle(0..45)
|
||
.output("tap_angle", "x.2c : non 45 degree angle tap")
|
||
tunm.ongrid(0.005).output("tunm_OFFGRID", "x.1b : OFFGRID vertex on tunm")
|
||
tunm.with_angle(0..45).output("tunm_angle", "x.3a : non 45 degree angle tunm")
|
||
poly.ongrid(0.005).output("poly_OFFGRID", "x.1b : OFFGRID vertex on poly")
|
||
poly.with_angle(0..90).output("poly_angle", "x.2 : non 90 degree angle poly")
|
||
rpm.ongrid(0.005).output("rpm_OFFGRID", "x.1b : OFFGRID vertex on rpm")
|
||
rpm.with_angle(0..45).output("rpm_angle", "x.3a : non 45 degree angle rpm")
|
||
npc.ongrid(0.005).output("npc_OFFGRID", "x.1b : OFFGRID vertex on npc")
|
||
npc.with_angle(0..45).output("npc_angle", "x.3a : non 45 degree angle npc")
|
||
nsdm.ongrid(0.005).output("nsdm_OFFGRID", "x.1b : OFFGRID vertex on nsdm")
|
||
nsdm.with_angle(0..45).output("nsdm_angle", "x.3a : non 45 degree angle nsdm")
|
||
psdm.ongrid(0.005).output("psdm_OFFGRID", "x.1b : OFFGRID vertex on psdm")
|
||
psdm.with_angle(0..45).output("psdm_angle", "x.3a : non 45 degree angle psdm")
|
||
licon.ongrid(0.005).output("licon_OFFGRID", "x.1b : OFFGRID vertex on licon")
|
||
licon.with_angle(0..90).output(
|
||
"licon_angle",
|
||
"x.2 : non 90 degree angle licon"
|
||
)
|
||
li.ongrid(0.005).output("li_OFFGRID", "x.1b : OFFGRID vertex on li")
|
||
li.with_angle(0..45).output("li_angle", "x.3a : non 45 degree angle li")
|
||
mcon.ongrid(0.005).output("ct_OFFGRID", "x.1b : OFFGRID vertex on mcon")
|
||
mcon.with_angle(0..90).output("ct_angle", "x.2 : non 90 degree angle mcon")
|
||
vpp.ongrid(0.005).output("vpp_OFFGRID", "x.1b : OFFGRID vertex on vpp")
|
||
vpp.with_angle(0..45).output("vpp_angle", "x.3a : non 45 degree angle vpp")
|
||
m1.ongrid(0.005).output("m1_OFFGRID", "x.1b : OFFGRID vertex on m1")
|
||
m1.with_angle(0..45).output("m1_angle", "x.3a : non 45 degree angle m1")
|
||
via.ongrid(0.005).output("via_OFFGRID", "x.1b : OFFGRID vertex on via")
|
||
via.with_angle(0..90).output("via_angle", "x.2 : non 90 degree angle via")
|
||
m2.ongrid(0.005).output("m2_OFFGRID", "x.1b : OFFGRID vertex on m2")
|
||
m2.with_angle(0..45).output("m2_angle", "x.3a : non 45 degree angle m2")
|
||
via2.ongrid(0.005).output("via2_OFFGRID", "x.1b : OFFGRID vertex on via2")
|
||
via2.with_angle(0..90).output("via2_angle", "x.2 : non 90 degree angle via2")
|
||
m3.ongrid(0.005).output("m3_OFFGRID", "x.1b : OFFGRID vertex on m3")
|
||
m3.with_angle(0..45).output("m3_angle", "x.3a : non 45 degree angle m3")
|
||
via3.ongrid(0.005).output("via3_OFFGRID", "x.1b : OFFGRID vertex on via3")
|
||
via3.with_angle(0..90).output("via3_angle", "x.2 : non 90 degree angle via3")
|
||
nsm.ongrid(0.005).output("nsm_OFFGRID", "x.1b : OFFGRID vertex on nsm")
|
||
nsm.with_angle(0..45).output("nsm_angle", "x.3a : non 45 degree angle nsm")
|
||
m4.ongrid(0.005).output("m4_OFFGRID", "x.1b : OFFGRID vertex on m4")
|
||
m4.with_angle(0..45).output("m4_angle", "x.3a : non 45 degree angle m4")
|
||
via4.ongrid(0.005).output("via4_OFFGRID", "x.1b : OFFGRID vertex on via4")
|
||
via4.with_angle(0..90).output("via4_angle", "x.2 : non 90 degree angle via4")
|
||
m5.ongrid(0.005).output("m5_OFFGRID", "x.1b : OFFGRID vertex on m5")
|
||
m5.with_angle(0..45).output("m5_angle", "x.3a : non 45 degree angle m5")
|
||
pad.ongrid(0.005).output("pad_OFFGRID", "x.1b : OFFGRID vertex on pad")
|
||
pad.with_angle(0..45).output("pad_angle", "x.3a : non 45 degree angle pad")
|
||
mf.ongrid(0.005).output("mf_OFFGRID", "x.1b : OFFGRID vertex on mf")
|
||
mf.with_angle(0..90).output("mf_angle", "x.2 : non 90 degree angle mf")
|
||
hvi.ongrid(0.005).output("hvi_OFFGRID", "x.1b : OFFGRID vertex on hvi")
|
||
hvi.with_angle(0..45).output("hvi_angle", "x.3a : non 45 degree angle hvi")
|
||
hvntm.ongrid(0.005).output("hvntm_OFFGRID", "x.1b : OFFGRID vertex on hvntm")
|
||
hvntm.with_angle(0..45).output(
|
||
"hvntm_angle",
|
||
"x.3a : non 45 degree angle hvntm"
|
||
)
|
||
vhvi.ongrid(0.005).output("vhvi_OFFGRID", "x.1b : OFFGRID vertex on vhvi")
|
||
vhvi.with_angle(0..45).output("vhvi_angle", "x.3a : non 45 degree angle vhvi")
|
||
uhvi.ongrid(0.005).output("uhvi_OFFGRID", "x.1b : OFFGRID vertex on uhvi")
|
||
uhvi.with_angle(0..45).output("uhvi_angle", "x.3a : non 45 degree angle uhvi")
|
||
pwell_rs.ongrid(0.005).output(
|
||
"pwell_rs_OFFGRID",
|
||
"x.1b : OFFGRID vertex on pwell_rs"
|
||
)
|
||
pwell_rs.with_angle(0..45).output(
|
||
"pwell_rs_angle",
|
||
"x.3a : non 45 degree angle pwell_rs"
|
||
)
|
||
areaid_re.ongrid(0.005).output(
|
||
"areaid_re_OFFGRID",
|
||
"x.1b : OFFGRID vertex on areaid.re"
|
||
)
|
||
end #OFFGRID
|
||
logger.info(" ")
|
||
logger.info("Cell exclusion list:")
|
||
logger.info(" rule | cell")
|
||
if SRAM_EXCLUDE
|
||
logger.info(
|
||
" nwell.6 | sky130_fd_io__gpiov2_amux, sky130_fd_io__simple_pad_and_busses, sram"
|
||
)
|
||
logger.info(" nsd.1 | sram")
|
||
logger.info(" nsd.2 | sram")
|
||
logger.info(" psd.1 | sram")
|
||
logger.info(" psd.2 | sram")
|
||
else
|
||
logger.info(
|
||
" nwell.6 | sky130_fd_io__gpiov2_amux, sky130_fd_io__simple_pad_and_busses"
|
||
)
|
||
end #SRAM_EXCLUDE
|
||
logger.info(" ")
|
||
logger.info("release #{release}")
|