chore(precheck): update klayout sky130A DRC deck

following chipfoundry/mpw_precheck#101
This commit is contained in:
Uri Shaked
2025-11-08 08:48:14 +02:00
parent df18cf8be8
commit b1fca8f72d
2 changed files with 297 additions and 157 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
sky130A_mr.drc from https://github.com/chipfoundry/mpw_precheck revision 3467a4c8252aa884cddc7d3f73370df9744dd65e sky130A_mr.drc from https://github.com/chipfoundry/mpw_precheck revision f2264a82c1686e2924422ec1dddb829b9601a86d
pin_label_purposes_overlapping_drawing.rb.drc from https://github.com/chipfoundry/mpw_precheck revision f6b9c3d3f00694f96dce8444149449b4719180f0 pin_label_purposes_overlapping_drawing.rb.drc from https://github.com/chipfoundry/mpw_precheck revision f6b9c3d3f00694f96dce8444149449b4719180f0
zeroarea.rb.drc from https://github.com/chipfoundry/mpw_precheck revision 4fd5283b124e931c9e71219f47270075176f84e2 zeroarea.rb.drc from https://github.com/chipfoundry/mpw_precheck revision 4fd5283b124e931c9e71219f47270075176f84e2
nwell_urpm.drc from https://open-source-silicon.slack.com/archives/C05FW1VMY5A/p1729112262642699?thread_ts=1728921677.220509&cid=C05FW1VMY5A nwell_urpm.drc from https://open-source-silicon.slack.com/archives/C05FW1VMY5A/p1729112262642699?thread_ts=1728921677.220509&cid=C05FW1VMY5A
+296 -156
View File
@@ -30,8 +30,10 @@
# * Rename `licon` checks # * Rename `licon` checks
# 2024-9-1 : # 2024-9-1 :
# * Change the default of sram_exclude to false # * 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 = "2024.2.11_01.09" release = "2025.10.29_01.10"
require "time" require "time"
require "logger" require "logger"
@@ -114,6 +116,13 @@ if $sram_exclude == "0" || $sram_exclude == "false"
else else
SRAM_EXCLUDE = true SRAM_EXCLUDE = true
end end
if $info == "0" || $info == "false"
INFO = false
else
INFO = true # front-end-of-line checks
end
logger.info("Args:") logger.info("Args:")
logger.info("\tsram_exclude: #{$sram_exclude}") logger.info("\tsram_exclude: #{$sram_exclude}")
logger.info("\tfeol: #{$feol}") logger.info("\tfeol: #{$feol}")
@@ -286,14 +295,19 @@ if FEOL
log("FEOL section") log("FEOL section")
# dnwell # dnwell
log("START: 64/18 (dnwell)") log("START: 64/18 (dnwell)")
dnwell.width(3.0, euclidian).output( dnwell
"dnwell.1", .width(3.0, euclidian)
"dnwell.1: min. dnwell width : 3.0um" .output(
"MRW_dnwell.WID.1",
"MRW_dnwell.WID.1: min. dnwell width : 3.0um"
) )
dnwell dnwell
.not(uhvi.or(vhvi)) .not(uhvi.or(vhvi))
.space(6.3, euclidian) .space(6.3, euclidian)
.output("dnwell.2", "dnwell.2 : min. dnwell spacing : 6.3um") .output(
"MRW_dnwell.SP.1",
"MRW_dnwell.SP.1 : min. dnwell spacing : 6.3um"
)
log("END: 64/18 (dnwell)") log("END: 64/18 (dnwell)")
not_sram = layout(source.cell_obj).select("-*sky130_sram_*kbyte_*") not_sram = layout(source.cell_obj).select("-*sky130_sram_*kbyte_*")
@@ -313,34 +327,34 @@ if FEOL
# nwell # nwell
log("START: 64/20 (nwell)") log("START: 64/20 (nwell)")
nwell.width(0.84, euclidian).output( nwell.width(0.84, euclidian).output(
"nwell.1", "MR_nwell.WID.1",
"nwell.1 : min. nwell width : 0.84um" "MR_nwell.WID.1 : min. nwell width : 0.84um"
) )
nwell.space(1.27, euclidian).output( nwell.space(1.27, euclidian).output(
"nwell.2a", "MR_nwell.SP.1",
"nwell.2a : min. nwell spacing (merged if less) : 1.27um" "MR_nwell.SP.1 : min. nwell spacing (merged if less) : 1.27um"
) )
nwell_interact = not_sram_nwell.and(not_io_nwell).merge nwell_interact = not_sram_nwell.and(not_io_nwell).merge
dnwell.enclosing(nwell_interact.holes, 1.03, euclidian).output( dnwell.enclosing(nwell_interact.holes, 1.03, euclidian).output(
"nwell.6", "MR_nwell.ENC.1",
"nwell.6 : min enclosure of nwellHole by dnwell : 1.03um" "MR_nwell.ENC.1 : min enclosure of nwellHole by dnwell : 1.03um"
) )
hvmarker = hvi.or(rdl).or(uhvi).or(vhvi) hvmarker = hvi.or(rdl).or(uhvi).or(vhvi)
nwell nwell
.interacting(nwell.and(hvmarker)) .interacting(nwell.and(hvmarker))
.not(hvmarker) .not(hvmarker)
.output("nwell.9", "nwell.9 : HVnwell must be enclosed by hv marker") .output("MR_nwell.CON.1", "MR_nwell.CON.1 : HVnwell must be enclosed by hv marker")
log("END: 64/20 (nwell)") log("END: 64/20 (nwell)")
# hvtp # hvtp
log("START: 78/44 (hvtp)") log("START: 78/44 (hvtp)")
hvtp.width(0.38, euclidian).output( hvtp.width(0.38, euclidian).output(
"hvtp.1", "MR_hvtp.WID.1",
"hvtp.1 : min. hvtp width : 0.38um" "MR_hvtp.WID.1 : min. hvtp width : 0.38um"
) )
hvtp.space(0.38, euclidian).output( hvtp.space(0.38, euclidian).output(
"hvtp.2", "MR_hvtp.SP.1",
"hvtp.2 : min. hvtp spacing : 0.38um" "MR_hvtp.SP.1 : min. hvtp spacing : 0.38um"
) )
log("END: 78/44 (hvtp)") log("END: 78/44 (hvtp)")
@@ -353,7 +367,7 @@ if FEOL
pwde pwde
.and(uhvi.or(vhvi)) .and(uhvi.or(vhvi))
.space(1.27, euclidian) .space(1.27, euclidian)
.output("pwde.2", "pwde.2 : min. pwde inside v20 spacing : 1.27um") .output("MR_pwde.SP.1", "MR_pwde.SP.1 : min. pwde inside v20 spacing : 1.27um")
log("END: 124/20 (pwde)") log("END: 124/20 (pwde)")
# hvtr # hvtr
@@ -366,7 +380,7 @@ if FEOL
"hvtr.2", "hvtr.2",
"hvtr.2 : min. hvtr spacing : 0.38um" "hvtr.2 : min. hvtr spacing : 0.38um"
) )
hvtr.and(hvtp).output("hvtr.2_a", "hvtr.2_a : hvtr must not overlap hvtp") hvtr.and(hvtp).output("MR_hvtr.CON.1", "MR_hvtr.CON.1 : hvtr must not overlap hvtp")
log("END: 18/20 (htvr)") log("END: 18/20 (htvr)")
# lvtn # lvtn
@@ -389,7 +403,7 @@ if FEOL
# ncm # ncm
log("START: 92/44 (ncm)") log("START: 92/44 (ncm)")
ncm.width(0.38, euclidian).output("ncm.1", "ncm.1 : min. ncm width : 0.38um") 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.space(0.38, euclidian).output(
"ncm.2a", "ncm.2a",
"ncm.2a : min. ncm spacing : 0.38um" "ncm.2a : min. ncm spacing : 0.38um"
@@ -439,7 +453,7 @@ if FEOL
tap tap
.not(areaid_ce) .not(areaid_ce)
.width(0.15, euclidian) .width(0.15, euclidian)
.output("difftap.1_c", "difftap.1_c : min. tap width in periphery : 0.15um") .output("MR_diff.WID.2", "MR_diff.WID.2 : min. tap width in periphery : 0.15um")
log("END: 65/44 (tap)") log("END: 65/44 (tap)")
difftap.space(0.27, euclidian).output( difftap.space(0.27, euclidian).output(
@@ -474,29 +488,57 @@ if FEOL
"thkox.2", "thkox.2",
"thkox.2 : min. thkox spacing inside periphery : 0.7um" "thkox.2 : min. thkox spacing inside periphery : 0.7um"
) )
thkox # thkox
hvi
.interacting(diff) .interacting(diff)
.not_inside(diff) .not_inside(diff)
.edges .edges
.and(diff) .and(diff)
.output("MR_thkox.CON.1", "MR_thkox.CON.1 : thkox must not straddle 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)") log("END: 80/20 (thkox)")
# poly # poly
log("START: 66/20 (poly)") log("START: 66/20 (poly)")
poly.width(0.15, euclidian).output( poly.width(0.15, euclidian).output(
"poly.1a", "MR_poly.WID.2",
"poly.1a : min. poly width : 0.15um" "MR_poly.WID.2 : min. poly width : 0.15um"
) )
poly poly
.not(areaid_ce) .not(areaid_ce)
.space(0.21, euclidian) .space(0.21, euclidian)
.output("poly.2", "poly.2 : min. poly spacing : 0.21um") .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 core_poly_gap = poly.inside(areaid_ce).drc(width(projection) <= 0.15).polygons
poly poly
.interacting(core_poly_gap) .interacting(core_poly_gap)
.isolated(0.16, projection) .isolated(0.16, projection)
.output("poly.3", "poly.3 : min. poly spacing inside areaid:core : 0.16um") .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)") log("END: 66/20 (poly)")
# ldntm # ldntm
@@ -515,37 +557,37 @@ if FEOL
# rpm # rpm
log("START: 86/20 (rpm)") log("START: 86/20 (rpm)")
rpm.width(1.27, euclidian).output( rpm.width(1.27, euclidian).output(
"rpm.1a", "MR_rpm.WID.1",
"rpm.1a : min. rpm width : 1.27um" "MR_rpm.WID.1 : min. rpm width : 1.27um"
) )
rpm.space(0.84, euclidian).output( rpm.space(0.84, euclidian).output(
"rpm.2", "MR_rpm.SP.1",
"rpm.2 : min. rpm spacing : 0.84um" "MR_rpm.SP.1 : min. rpm spacing : 0.84um"
) )
log("END: 86/20 (rpm)") log("END: 86/20 (rpm)")
# rdl # rdl
log("START: 74/20 (rdl)") log("START: 74/20 (rdl)")
rdl.width(10.um, euclidian).output("rdl.1", "rdl.1 : min. rdl width : 10um") rdl.width(10.um, euclidian).output("MR_rdl.WID.1", "MR_rdl.WID.1 : min. rdl width : 10um")
rdl.space(10.um, euclidian).output("rdl.2", "rdl.2 : min. rdl spacing : 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") rdl.output("MR_rdl.CON.1", "MR_rdl.CON.1 : use of rdl layer is prohibited")
log("END: 74/20 (rdl)") log("END: 74/20 (rdl)")
# urpm # urpm
log("START: 79/20 (urpm)") log("START: 79/20 (urpm)")
urpm.width(1.27, euclidian).output( urpm.width(1.27, euclidian).output(
"urpm.1a", "MR_urpm.WID.1",
"urpm.1a : min. rpm width : 1.27um" "MR_urpm.WID.1 : min. rpm width : 1.27um"
) )
urpm.space(0.84, euclidian).output( urpm.space(0.84, euclidian).output(
"urpm.2", "MR_urpm.SP.1",
"urpm.2 : min. rpm spacing : 0.84um" "MR_urpm.SP.1 : min. rpm spacing : 0.84um"
) )
log("END: 79/20 (urpm)") log("END: 79/20 (urpm)")
# npc # npc
log("START: 95/20 (npc)") log("START: 95/20 (npc)")
npc.width(0.27, euclidian).output("npc.1", "npc.1 : min. npc width : 0.27um") 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.space(0.27, euclidian).output(
"npc.2", "npc.2",
"npc.2 : min. npc spacing, should be manually merged if less than : 0.27um" "npc.2 : min. npc spacing, should be manually merged if less than : 0.27um"
@@ -558,11 +600,11 @@ if FEOL
not_sram_nsdm not_sram_nsdm
.not(areaid_ce) .not(areaid_ce)
.space(0.38, euclidian) .space(0.38, euclidian)
.output("nsdm.1", "nsdm.1 : min. nsdm spacing in periphery : 0.38um") .output("MR_nsdm.SP.1", "MR_nsdm.SP.1 : min. nsdm spacing in periphery : 0.38um")
not_sram_nsdm not_sram_nsdm
.outside(areaid_ce) .outside(areaid_ce)
.width(0.38, euclidian) .width(0.38, euclidian)
.output("nsdm.2", "nsdm.2 : min. nsdm width in periphery : 0.38um") .output("MR_nsdm.WID.2", "MR_nsdm.WID.2 : min. nsdm width in periphery : 0.38um")
not_sram_nsdm_width = not_sram_nsdm_width =
not_sram_nsdm not_sram_nsdm
.width(0.38, euclidian) .width(0.38, euclidian)
@@ -570,7 +612,7 @@ if FEOL
.interacting(areaid_ce_merged) .interacting(areaid_ce_merged)
not_sram_nsdm_width.interacting( not_sram_nsdm_width.interacting(
not_sram_nsdm_width.edges.outside_part(areaid_ce_merged) not_sram_nsdm_width.edges.outside_part(areaid_ce_merged)
).output("nsdm.3", "nsdm.3 : min. diff width across areaid:ce : 0.38um") ).output("MR_nsdm.WID.1", "MR_nsdm.WID.1 : min. diff width across areaid:ce : 0.38um")
nsdm_space = nsdm_space =
not_sram_nsdm not_sram_nsdm
.space(0.38, euclidian) .space(0.38, euclidian)
@@ -578,15 +620,15 @@ if FEOL
.interacting(areaid_ce_merged) .interacting(areaid_ce_merged)
nsdm_space.interacting( nsdm_space.interacting(
nsdm_space.edges.outside_part(areaid_ce_merged) nsdm_space.edges.outside_part(areaid_ce_merged)
).output("nsdm.4", "nsdm.4 : min. spacing across areaid:ce : 0.38um") ).output("MR_nsdm.SP.2", "MR_nsdm.SP.2 : min. spacing across areaid:ce : 0.38um")
not_sram_nsdm not_sram_nsdm
.inside(areaid_ce) .inside(areaid_ce)
.width(0.29, euclidian) .width(0.29, euclidian)
.output("nsdm.5", "nsdm.5 : min. nsdm width in areaid:ce : 0.29um") .output("MR_nsdm.WID.3", "MR_nsdm.WID.3 : min. nsdm width in areaid:ce : 0.29um")
not_sram_nsdm not_sram_nsdm
.inside(areaid_ce) .inside(areaid_ce)
.space(0.29, euclidian) .space(0.29, euclidian)
.output("nsdm.6", "nsdm.6 : min. nsdm spacing in areaid:ce : 0.29um") .output("MR_nsdm.SP.3", "MR_nsdm.SP.3 : min. nsdm spacing in areaid:ce : 0.29um")
not_sram_nsdm not_sram_nsdm
.inside(areaid_ce) .inside(areaid_ce)
.space(0.38, projection) .space(0.38, projection)
@@ -599,29 +641,29 @@ if FEOL
nsdm nsdm
.not(areaid_ce) .not(areaid_ce)
.space(0.38, euclidian) .space(0.38, euclidian)
.output("nsdm.1", "nsdm.1 : min. nsdm spacing in periphery : 0.38um") .output("MR_nsdm.SP.1", "MR_nsdm.SP.1 : min. nsdm spacing in periphery : 0.38um")
nsdm nsdm
.outside(areaid_ce) .outside(areaid_ce)
.width(0.38, euclidian) .width(0.38, euclidian)
.output("nsdm.2", "nsdm.2 : min. nsdm width in periphery : 0.38um") .output("MR_nsdm.WID.2", "MR_nsdm.WID.2 : min. nsdm width in periphery : 0.38um")
nsdm_width = nsdm_width =
nsdm.width(0.38, euclidian).polygons.interacting(areaid_ce_merged) nsdm.width(0.38, euclidian).polygons.interacting(areaid_ce_merged)
nsdm_width.interacting( nsdm_width.interacting(
nsdm_width.edges.outside_part(areaid_ce_merged) nsdm_width.edges.outside_part(areaid_ce_merged)
).output("nsdm.3", "nsdm.3 : min. diff width across areaid:ce : 0.38um") ).output("MR_nsdm.WID.1", "MR_nsdm.WID.1 : min. diff width across areaid:ce : 0.38um")
nsdm_space = nsdm_space =
nsdm.space(0.38, euclidian).polygons.interacting(areaid_ce_merged) nsdm.space(0.38, euclidian).polygons.interacting(areaid_ce_merged)
nsdm_space.interacting( nsdm_space.interacting(
nsdm_space.edges.outside_part(areaid_ce_merged) nsdm_space.edges.outside_part(areaid_ce_merged)
).output("nsdm.4", "nsdm.4 : min. spacing across areaid:ce : 0.38um") ).output("MR_nsdm.SP.2", "MR_nsdm.SP.2 : min. spacing across areaid:ce : 0.38um")
nsdm nsdm
.inside(areaid_ce) .inside(areaid_ce)
.width(0.29, euclidian) .width(0.29, euclidian)
.output("nsdm.5", "nsdm.5 : min. nsdm width in areaid:ce : 0.29um") .output("MR_nsdm.WID.3", "MR_nsdm.WID.3 : min. nsdm width in areaid:ce : 0.29um")
nsdm nsdm
.inside(areaid_ce) .inside(areaid_ce)
.space(0.29, euclidian) .space(0.29, euclidian)
.output("nsdm.6", "nsdm.6 : min. nsdm spacing in areaid:ce : 0.29um") .output("MR_nsdm.SP.3", "MR_nsdm.SP.3 : min. nsdm spacing in areaid:ce : 0.29um")
nsdm nsdm
.inside(areaid_ce) .inside(areaid_ce)
.space(0.38, projection) .space(0.38, projection)
@@ -640,11 +682,11 @@ if FEOL
not_sram_psdm not_sram_psdm
.not(areaid_ce) .not(areaid_ce)
.space(0.38, euclidian) .space(0.38, euclidian)
.output("psdm.1", "psdm.1 : min. psdm spacing in periphery : 0.38um") .output("MR_psdm.SP.1", "MR_psdm.SP.1 : min. psdm spacing in periphery : 0.38um")
not_sram_psdm not_sram_psdm
.outside(areaid_ce) .outside(areaid_ce)
.width(0.38, euclidian) .width(0.38, euclidian)
.output("psdm.2", "psdm.2 : min. psdm width in periphery : 0.38um") .output("MR_psdm.WID.2", "MR_psdm.WID.2 : min. psdm width in periphery : 0.38um")
not_sram_psdm_width = not_sram_psdm_width =
not_sram_psdm not_sram_psdm
.width(0.38, euclidian) .width(0.38, euclidian)
@@ -652,7 +694,7 @@ if FEOL
.interacting(areaid_ce_merged) .interacting(areaid_ce_merged)
not_sram_psdm_width.interacting( not_sram_psdm_width.interacting(
not_sram_psdm_width.edges.outside_part(areaid_ce_merged) not_sram_psdm_width.edges.outside_part(areaid_ce_merged)
).output("psdm.3", "psdm.3 : min. diff width across areaid:ce : 0.38um") ).output("MR_psdm.WID.1", "MR_psdm.WID.1 : min. diff width across areaid:ce : 0.38um")
psdm_space = psdm_space =
not_sram_psdm not_sram_psdm
.space(0.38, euclidian) .space(0.38, euclidian)
@@ -660,15 +702,15 @@ if FEOL
.interacting(areaid_ce_merged) .interacting(areaid_ce_merged)
psdm_space.interacting( psdm_space.interacting(
psdm_space.edges.outside_part(areaid_ce_merged) psdm_space.edges.outside_part(areaid_ce_merged)
).output("psdm.4", "psdm.4 : min. spacing across areaid:ce : 0.38um") ).output("MR_psdm.SP.2", "MR_psdm.SP.2 : min. spacing across areaid:ce : 0.38um")
not_sram_psdm not_sram_psdm
.inside(areaid_ce) .inside(areaid_ce)
.width(0.29, euclidian) .width(0.29, euclidian)
.output("psdm.5", "psdm.5 : min. psdm width in areaid:ce : 0.29um") .output("MR_psdm.WID.3", "MR_psdm.WID.3 : min. psdm width in areaid:ce : 0.29um")
not_sram_psdm not_sram_psdm
.inside(areaid_ce) .inside(areaid_ce)
.space(0.29, euclidian) .space(0.29, euclidian)
.output("psdm.6", "psdm.6 : min. psdm spacing in areaid:ce : 0.29um") .output("MR_psdm.SP.3", "MR_psdm.SP.3 : min. psdm spacing in areaid:ce : 0.29um")
not_sram_psdm not_sram_psdm
.inside(areaid_ce) .inside(areaid_ce)
.space(0.38, projection) .space(0.38, projection)
@@ -688,29 +730,29 @@ if FEOL
psdm psdm
.not(areaid_ce) .not(areaid_ce)
.space(0.38, euclidian) .space(0.38, euclidian)
.output("psdm.1", "psdm.1 : min. psdm spacing in periphery : 0.38um") .output("MR_psdm.SP.1", "MR_psdm.SP.1 : min. psdm spacing in periphery : 0.38um")
psdm psdm
.outside(areaid_ce) .outside(areaid_ce)
.width(0.38, euclidian) .width(0.38, euclidian)
.output("psdm.2", "psdm.2 : min. psdm width in periphery : 0.38um") .output("MR_psdm.WID.2", "MR_psdm.WID.2 : min. psdm width in periphery : 0.38um")
psdm_width = psdm_width =
psdm.width(0.38, euclidian).polygons.interacting(areaid_ce_merged) psdm.width(0.38, euclidian).polygons.interacting(areaid_ce_merged)
psdm_width.interacting( psdm_width.interacting(
psdm_width.edges.outside_part(areaid_ce_merged) psdm_width.edges.outside_part(areaid_ce_merged)
).output("psdm.3", "psdm.3 : min. diff width across areaid:ce : 0.38um") ).output("MR_psdm.WID.1", "MR_psdm.WID.1 : min. diff width across areaid:ce : 0.38um")
psdm_space = psdm_space =
psdm.space(0.38, euclidian).polygons.interacting(areaid_ce_merged) psdm.space(0.38, euclidian).polygons.interacting(areaid_ce_merged)
psdm_space.interacting( psdm_space.interacting(
psdm_space.edges.outside_part(areaid_ce_merged) psdm_space.edges.outside_part(areaid_ce_merged)
).output("psdm.4", "psdm.4 : min. spacing across areaid:ce : 0.38um") ).output("MR_psdm.SP.2", "MR_psdm.SP.2 : min. spacing across areaid:ce : 0.38um")
psdm psdm
.inside(areaid_ce) .inside(areaid_ce)
.width(0.29, euclidian) .width(0.29, euclidian)
.output("psdm.5", "psdm.5 : min. psdm width in areaid:ce : 0.29um") .output("MR_psdm.WID.3", "MR_psdm.WID.3 : min. psdm width in areaid:ce : 0.29um")
psdm psdm
.inside(areaid_ce) .inside(areaid_ce)
.space(0.29, euclidian) .space(0.29, euclidian)
.output("psdm.6", "psdm.6 : min. psdm spacing in areaid:ce : 0.29um") .output("MR_psdm.SP.3", "MR_psdm.SP.3 : min. psdm spacing in areaid:ce : 0.29um")
psdm psdm
.inside(areaid_ce) .inside(areaid_ce)
.space(0.38, projection) .space(0.38, projection)
@@ -735,6 +777,18 @@ if FEOL
polyi = poly_fill.and(poly) polyi = poly_fill.and(poly)
poly_licon = (polyi.and(licon)).and(areaid_ce) poly_licon = (polyi.and(licon)).and(areaid_ce)
licon_peri = licon.outside(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 if SEAL
ringLICON = licon.drc(with_holes > 0) ringLICON = licon.drc(with_holes > 0)
rectLICON = licon.not(ringLICON) rectLICON = licon.not(ringLICON)
@@ -789,94 +843,180 @@ if FEOL
"licon.6", "licon.6",
"licon.6 : npc min enclosure of poly_licon inside areaid:core : 0.045um" "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_all = licon.and(poly)
poly_licon_all.separation(psdm, 0.11, euclidian).output(
"MR_licon.SP.6",
"MR_licon.SP.6 : poly_licon minimum space to psdm (overlap prohibited) : 0.11um"
)
# 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)") log("END: 66/44 (licon)")
#nsm #nsm
log("START: 61/20 (nsm)") log("START: 61/20 (nsm)")
nsm.space(4.0, euclidian).output("nsm.1", "nsm.1 : nsm min. spacing : 4.0um") 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("nsm.2", "nsm.2 : nsm min. width : 3.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)") log("END: 61/20 (nsm)")
# CAPM # CAPM
log("START: 89/44 (capm)") log("START: 89/44 (capm)")
capm.width(1.0, euclidian).output( capm.width(1.0, euclidian).output(
"capm.1", "MR_capm.WID.1",
"capm.1 : min. capm width : 1.0um" "MR_capm.WID.1 : min. capm width : 1.0um"
) )
capm.space(0.84, euclidian).output( capm.space(0.84, euclidian).output(
"capm.2a", "MR_capm.SP.1",
"capm.2a : min. capm spacing : 0.84um" "MR_capm.SP.1 : min. capm spacing : 0.84um"
) )
m3_bot_plate = ((capm.and(m3)).sized(0.14)) m3_bot_plate = ((capm.and(m3)).sized(0.14))
m3_bot_plate m3_bot_plate
.isolated(1.2, euclidian) .isolated(1.2, euclidian)
.polygons .polygons
.not(m3) .not(m3)
.output("capm.2b", "capm.2b : min. spacing between met3_bot_plate : 1.2um") .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.not_interacting(m3_bot_plate)).separation(
m3_bot_plate, m3_bot_plate,
1.2, 1.2,
euclidian euclidian
).output( ).output(
"capm.2b_a", "MR_capm.SP.2",
"capm.2b_a : min. spacing between met3_bot_plate and met3 : 1.2um" "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") # 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( m3.enclosing(capm, 0.14, euclidian).output(
"capm.3", "MR_capm.ENC.1",
"capm.3 : min. m3 enclosure of capm : 0.14um" "MR_capm.ENC.1 : min. m3 enclosure of capm : 0.14um"
) )
capm.enclosing(via3, 0.14, euclidian).output( capm.enclosing(via3, 0.14, euclidian).output(
"capm.4", "MR_capm.ENC.2",
"capm.4 : min. capm enclosure of via3 : 0.14um" "MR_capm.ENC.2 : min. capm enclosure of via3 : 0.14um"
) )
capm.separation(via3, 0.14, euclidian).output( capm.separation(via3, 0.14, euclidian).output(
"capm.5", "MR_capm.SP.3",
"capm.5 : min. capm spacing to via3 : 0.14um" "MR_capm.SP.3 : min. capm spacing to via3 : 0.14um"
) )
(m3.not_interacting(capm)).separation(capm, 0.5, euclidian).output( (m3.not_interacting(capm)).separation(capm, 0.5, euclidian).output(
"capm.11", "MR_capm.SP.2",
"capm.11 : Min spacing of capm and met3 not overlapping capm : 0.5um" "MR_capm.SP.2 : Min spacing of capm and met3 not overlapping capm : 0.5um"
) )
log("END: 89/44 (capm)") log("END: 89/44 (capm)")
# CAP2M # CAP2M
log("START: 97/44 (cap2m)") log("START: 97/44 (cap2m)")
cap2m.width(1.0, euclidian).output( cap2m.width(1.0, euclidian).output(
"cap2m.1", "MR_cap2m.WID.1",
"cap2m.1 : min. cap2m width : 1.0um" "MR_cap2m.WID.1 : min. cap2m width : 1.0um"
) )
cap2m.space(0.84, euclidian).output( cap2m.space(0.84, euclidian).output(
"cap2m.2a", "MR_cap2m.SP.1",
"cap2m.2a : min. cap2m spacing : 0.84um" "MR_cap2m.SP.1 : min. cap2m spacing : 0.84um"
) )
m4 m4
.interacting(cap2m) .interacting(cap2m)
.isolated(1.2, euclidian) .isolated(1.2, euclidian)
.output("cap2m.2b", "cap2m.2b : min. cap2m spacing : 1.2um") .output("MR_cap2m.SP.2", "MR_cap2m.SP.2 : min. cap2m spacing : 1.2um")
(m4.interacting(cap2m)).isolated(1.2, euclidian).output( (m4.interacting(cap2m)).isolated(1.2, euclidian).output(
"cap2m.2b_a", "MR_cap2m.SP.2",
"cap2m.2b_a : min. spacing of m4_bot_plate : 1.2um" "MR_cap2m.SP.2 : min. spacing of m4_bot_plate : 1.2um"
) )
cap2m cap2m
.and(m4) .and(m4)
.enclosing(m4, 0.14, euclidian) .enclosing(m4, 0.14, euclidian)
.output("cap2m.3", "cap2m.3 : min. m4 enclosure of cap2m : 0.14um") .output("MR_cap2m.ENC.1", "MR_cap2m.ENC.1 : min. m4 enclosure of cap2m : 0.14um")
m4.enclosing(cap2m, 0.14, euclidian).output( m4.enclosing(cap2m, 0.14, euclidian).output(
"cap2m.3_a", "MR_cap2m.ENC.1",
"cap2m.3_a : min. m4 enclosure of cap2m : 0.14um" "MR_cap2m.ENC.1 : min. m4 enclosure of cap2m : 0.14um"
) )
cap2m.enclosing(via4, 0.2, euclidian).output( cap2m.enclosing(via4, 0.2, euclidian).output(
"cap2m.4", "MR_cap2m.ENC.2",
"cap2m.4 : min. cap2m enclosure of via4 : 0.2um" "MR_cap2m.ENC.2 : min. cap2m enclosure of via4 : 0.2um"
) )
cap2m.separation(via4, 0.2, euclidian).output( cap2m.separation(via4, 0.2, euclidian).output(
"cap2m.5", "MR_cap2m.SP.3",
"cap2m.5 : min. cap2m spacing to via4 : 0.2um" "MR_cap2m.SP.3 : min. cap2m spacing to via4 : 0.2um"
) )
(m4.not_interacting(cap2m)).separation(cap2m, 0.5, euclidian).output( (m4.not_interacting(cap2m)).separation(cap2m, 0.5, euclidian).output(
"cap2m.11", "MR_cap2m.SP.2",
"cap2m.11 : Min spacing of cap2m and met4 not overlapping cap2m : 0.5um" "MR_cap2m.SP.2 : Min spacing of cap2m and met4 not overlapping cap2m : 0.5um"
) )
log("END: 97/44 (cap2m)") log("END: 97/44 (cap2m)")
end #FEOL end #FEOL
@@ -942,55 +1082,55 @@ if BEOL
end end
rectMCON_peri = rectMCON.outside(areaid_ce) rectMCON_peri = rectMCON.outside(areaid_ce)
rectMCON.non_rectangles.output( rectMCON.non_rectangles.output(
"ct.1", "MR_mcon.ANG.1",
"ct.1: non-ring mcon should be rectangular" "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.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 < 0.17).output( rectMCON_peri.drc(width(euclidian) < 0.17).output(
"ct.1_a", "MR_mcon.WID.1",
"ct.1_a : minimum width of mcon : 0.17um" "MR_mcon.WID.1 : minimum width of mcon : 0.17um"
) )
rectMCON_peri.drc(length > 0.17).output( rectMCON_peri.drc(length > 0.17).output(
"ct.1_b", "MR_mcon.LEN.1",
"ct.1_b : maximum length of mcon : 0.17um" "MR_mcon.LEN.1 : maximum length of mcon : 0.17um"
) )
mcon.space(0.19, euclidian).output( mcon.space(0.19, euclidian).output(
"ct.2", "MR_mcon.SP.1",
"ct.2 : min. mcon spacing : 0.19um" "MR_mcon.SP.1 : min. mcon spacing : 0.19um"
) )
if SEAL if SEAL
ringMCON.width(0.17, euclidian).output( ringMCON.width(0.17, euclidian).output(
"ct.3", "MR_mcon.WID.2",
"ct.3 : min. width of ring-shaped mcon : 0.17um" "MR_mcon.WID.2 : min. width of ring-shaped mcon : 0.17um"
) )
ringMCON.drc(width >= 0.175).output( ringMCON.drc(width(euclidian) >= 0.175).output(
"ct.3_a", "MR_mcon.WID.3",
"ct.3_a : max. width of ring-shaped mcon : 0.175um" "MR_mcon.WID.3 : max. width of ring-shaped mcon : 0.175um"
) )
ringMCON.not(areaid_sl).output( ringMCON.not(areaid_sl).output(
"ct.3_b", "MR_mcon.CON.1",
"ct.3_b: ring-shaped mcon must be enclosed by areaid_sl" "MR_mcon.CON.1: ring-shaped mcon must be enclosed by areaid_sl"
) )
end end
mconnotace.not(li).output("ct.4", "ct.4 : mcon should covered by li") mconnotace.not(li).output("MR_mcon.CON.10", "MR_mcon.CON.10 : mcon should covered by li")
log("END: 67/44 (mcon)") log("END: 67/44 (mcon)")
# m1 # m1
log("START: 68/20 (m1)") log("START: 68/20 (m1)")
m1.width(0.14, euclidian).output("m1.1", "m1.1 : min. m1 width : 0.14um") 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 huge_m1 = m1.sized(-1.5).sized(1.5).snap(0.005) & m1
non_huge_m1 = m1.edges - huge_m1 non_huge_m1 = m1.edges - huge_m1
huge_m1 = huge_m1.edges.outside_part(m1.merged) huge_m1 = huge_m1.edges.outside_part(m1.merged)
non_huge_m1.space(0.14, euclidian).output( non_huge_m1.space(0.14, euclidian).output(
"m1.2", "MR_met1.SP.1",
"m1.2 : min. m1 spacing : 0.14um" "MR_met1.SP.1 : min. m1 spacing : 0.14um"
) )
( (
huge_m1.separation(non_huge_m1, 0.28, euclidian) + huge_m1.separation(non_huge_m1, 0.28, euclidian) +
huge_m1.space(0.28, euclidian) huge_m1.space(0.28, euclidian)
).output("m1.3ab", "m1.3ab : min. 3um.m1 spacing m1 : 0.28um") ).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", "-s8fpls_pl8", "-s8fs_cmux4_fm")
not_in_cell6 = not_in_cell6 =
@@ -1004,12 +1144,12 @@ if BEOL
not_in_cell6_m1 = not_in_cell6.input(m1_wildcard) not_in_cell6_m1 = not_in_cell6.input(m1_wildcard)
not_in_cell6_m1.enclosing(mconnotace, 0.03, euclidian).output( not_in_cell6_m1.enclosing(mconnotace, 0.03, euclidian).output(
"791_m1.4", "MR_met1.ENC.1",
"791_m1.4 : min. m1 enclosure of mcon : 0.03um" "MR_met1.ENC.1 : min. m1 enclosure of mcon : 0.03um"
) )
mconnotace.not(m1).output( mconnotace.not(m1).output(
"m1.4", "MR_met1.CON.1",
"m1.4 : mcon periphery must be enclosed by m1" "MR_met1.CON.1 : mcon periphery must be enclosed by m1"
) )
in_cell6 = in_cell6 =
layout(source.cell_obj).select( layout(source.cell_obj).select(
@@ -1032,12 +1172,12 @@ if BEOL
"m1.4a_a : mcon periph must be enclosed by met1 for specific cells" "m1.4a_a : mcon periph must be enclosed by met1 for specific cells"
) )
m1.with_area(0..0.083).output("m1.6", "m1.6 : min. m1 area : 0.083um²") m1.with_area(0..0.083).output("MR_met1.AR.1", "MR_met1.AR.1 : min. m1 area : 0.083um²")
m1 m1
.holes .holes
.with_area(0..0.14) .with_area(0..0.14)
.output("m1.7", "m1.7 : min. m1 with holes area : 0.14um²") .output("MR_met1.AR.2", "MR_met1.AR.2 : min. m1 with holes area : 0.14um²")
if FLOATING_MET if FLOATING_MET
m1.not_interacting(via.or(mcon)).output( m1.not_interacting(via.or(mcon)).output(
"m1.x", "m1.x",
@@ -1045,7 +1185,7 @@ if BEOL
) )
end end
if backend_flow = AL if backend_flow == AL
#Could flag false positive, fix would be to add .rectangles for m1 #Could flag false positive, fix would be to add .rectangles for m1
mconnotace_edges_with_less_enclosure_m1 = mconnotace_edges_with_less_enclosure_m1 =
m1.enclosing(mconnotace, 0.06, projection).second_edges m1.enclosing(mconnotace, 0.06, projection).second_edges
@@ -1062,7 +1202,7 @@ if BEOL
# via # via
log("START: 68/44 (via)") log("START: 68/44 (via)")
if backend_flow = AL if backend_flow == AL
if SEAL if SEAL
ringVIA = via.drc(with_holes > 0) ringVIA = via.drc(with_holes > 0)
rectVIA = via.not(ringVIA) rectVIA = via.not(ringVIA)
@@ -1096,7 +1236,7 @@ if BEOL
"via.3", "via.3",
"via.3 : min. width of ring-shaped via : 0.2um" "via.3 : min. width of ring-shaped via : 0.2um"
) )
ringVIA.drc(width >= 0.205).output( ringVIA.drc(width(euclidian) >= 0.205).output(
"via.3_a", "via.3_a",
"via.3_a : max. width of ring-shaped via : 0.205um" "via.3_a : max. width of ring-shaped via : 0.205um"
) )
@@ -1109,12 +1249,12 @@ if BEOL
m1 m1
.edges .edges
.enclosing(rectVIA.drc(width == 0.15), 0.055, euclidian) .enclosing(rectVIA.drc(width == 0.15), 0.055, euclidian)
.output("via.4a", "via.4a : min. m1 enclosure of 0.15um via : 0.055um") .output("MR_via1.ENC.1", "MR_via1.ENC.1 : min. m1 enclosure of 0.15um via : 0.055um")
rectVIA rectVIA
.squares .squares
.drc(width == 0.15) .drc(width == 0.15)
.not(m1) .not(m1)
.output("via.4a_a", "via.4a_a : 0.15um via must be enclosed by met1") .output("MR_via1.CON.1", "MR_via1.CON.1 : 0.15um via must be enclosed by met1")
via1_edges_with_less_enclosure_m1 = via1_edges_with_less_enclosure_m1 =
m1 m1
@@ -1134,7 +1274,7 @@ if BEOL
# m2 # m2
log("START: 69/20 (m2)") log("START: 69/20 (m2)")
via_inside_periphery = via.and(areaid_ce) via_inside_periphery = via.and(areaid_ce)
m2.width(0.14, euclidian).output("m2.1", "m2.1 : min. m2 width : 0.14um") 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 huge_m2 = m2.sized(-1.5).sized(1.5).snap(0.005) & m2
non_huge_m2 = m2.edges - huge_m2 non_huge_m2 = m2.edges - huge_m2
@@ -1149,20 +1289,20 @@ if BEOL
( (
huge_m2.separation(non_huge_m2, 0.28, euclidian) + huge_m2.separation(non_huge_m2, 0.28, euclidian) +
huge_m2.space(0.28, euclidian) huge_m2.space(0.28, euclidian)
).output("m2.3ab", "m2.3ab : min. 3um.m2 spacing m2 : 0.28um") ).output("MRW_met2.SP.2", "MRW_met2.SP.2 : min. 3um.m2 spacing m2 : 0.28um")
m2.with_area(0..0.0676).output("m2.6", "m2.6 : min. m2 area : 0.0676um²") m2.with_area(0..0.0676).output("MR_met2.AR.1", "MR_met2.AR.1 : min. m2 area : 0.0676um²")
m2 m2
.holes .holes
.with_area(0..0.14) .with_area(0..0.14)
.output("m2.7", "m2.7 : min. m2 holes area : 0.14um²") .output("MR_met2.AR.2", "MR_met2.AR.2 : min. m2 holes area : 0.14um²")
if FLOATING_MET if FLOATING_MET
m2.not_interacting(via.or(via2)).output( m2.not_interacting(via.or(via2)).output(
"m2.x", "m2.x",
"floating met2, must interact with via1 or via2" "floating met2, must interact with via1 or via2"
) )
end end
if backend_flow = AL if backend_flow == AL
m2.enclosing(via_outside_periphery, 0.055, euclidian).output( m2.enclosing(via_outside_periphery, 0.055, euclidian).output(
"m2.4", "m2.4",
"m2.4 : min. m2 enclosure of via : 0.055um" "m2.4 : min. m2 enclosure of via : 0.055um"
@@ -1189,7 +1329,7 @@ if BEOL
# via2 # via2
log("START: 69/44 (via2)") log("START: 69/44 (via2)")
if backend_flow = AL if backend_flow == AL
if SEAL if SEAL
ringVIA2 = via2.drc(with_holes > 0) ringVIA2 = via2.drc(with_holes > 0)
rectVIA2 = via2.not(ringVIA2) rectVIA2 = via2.not(ringVIA2)
@@ -1209,7 +1349,7 @@ if BEOL
via2_not_mt via2_not_mt
.edges .edges
.without_length(nil, 0.2 + 1.dbu) .without_length(nil, 0.2 + 1.dbu)
.output("via2.1a_b", "via2.1a_b : maximum length of via2 : 0.2um") .output("MR_via2.LEN.1", "MR_via2.LEN.1 : maximum length of via2 : 0.2um")
via2.space(0.2, euclidian).output( via2.space(0.2, euclidian).output(
"via2.2", "via2.2",
"via2.2 : min. via2 spacing : 0.2um" "via2.2 : min. via2 spacing : 0.2um"
@@ -1220,7 +1360,7 @@ if BEOL
"via2.3", "via2.3",
"via2.3 : min. width of ring-shaped via2 : 0.2um" "via2.3 : min. width of ring-shaped via2 : 0.2um"
) )
ringVIA2.drc(width >= 0.205).output( ringVIA2.drc(width(euclidian) >= 0.205).output(
"via2.3_a", "via2.3_a",
"via2.3_a : max. width of ring-shaped via2 : 0.205um" "via2.3_a : max. width of ring-shaped via2 : 0.205um"
) )
@@ -1234,7 +1374,7 @@ if BEOL
"via2.4", "via2.4",
"via2.4 : min. m2 enclosure of via2 : 0.04um" "via2.4 : min. m2 enclosure of via2 : 0.04um"
) )
via2.not(m2).output("via2.4_a", "via2.4_a : via must be enclosed by met2") via2.not(m2).output("MR_via2.CON.1", "MR_via2.CON.1 : via must be enclosed by met2")
via2_edges_with_less_enclosure = via2_edges_with_less_enclosure =
m2.enclosing(via2, 0.085, projection).second_edges m2.enclosing(via2, 0.085, projection).second_edges
@@ -1250,7 +1390,7 @@ if BEOL
# m3 # m3
log("START: 70/20 (m3)") log("START: 70/20 (m3)")
m3.width(0.3, euclidian).output("m3.1", "m3.1 : min. m3 width : 0.3um") 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 huge_m3 = m3.sized(-1.5).sized(1.5).snap(0.005) & m3
non_huge_m3 = m3.edges - huge_m3 non_huge_m3 = m3.edges - huge_m3
@@ -1264,30 +1404,30 @@ if BEOL
( (
huge_m3.separation(non_huge_m3, 0.4, euclidian) + huge_m3.separation(non_huge_m3, 0.4, euclidian) +
huge_m3.space(0.4, euclidian) huge_m3.space(0.4, euclidian)
).output("m3.3cd", "m3.3cd : min. 3um.m3 spacing m3 : 0.4um") ).output("MRW_met3.SP.2", "MRW_met3.SP.2 : min. 3um.m3 spacing m3 : 0.4um")
if FLOATING_MET if FLOATING_MET
m3.not_interacting(via2.or(via3)).output( m3.not_interacting(via2.or(via3)).output(
"m3.x", "m3.x",
"floating met3, must interact with via2 or via3" "floating met3, must interact with via2 or via3"
) )
end end
if backend_flow = AL if backend_flow == AL
m3.enclosing(via2, 0.065, euclidian).output( m3.enclosing(via2, 0.065, euclidian).output(
"m3.4", "m3.4",
"m3.4 : min. m3 enclosure of via2 : 0.065um" "m3.4 : min. m3 enclosure of via2 : 0.065um"
) )
via2.not(m3).output("m3.4_a", "m3.4_a : via2 must be enclosed by met3") via2.not(m3).output("MR_met3.CON.1", "MR_met3.CON.1 : via2 must be enclosed by met3")
end end
m3.with_area(0..0.240).output("m3.6", "m3.6 : min. m3 area : 0.240um²") m3.with_area(0..0.240).output("MR_met3.AR.1", "MR_met3.AR.1 : min. m3 area : 0.240um²")
m3 m3
.holes .holes
.with_area(0..0.2) .with_area(0..0.2)
.output("m3.7", "m3.7 : min. m3 holes area : 0.2um²") .output("MR_met3.AR.2", "MR_met3.AR.2 : min. m3 holes area : 0.2um²")
log("END: 70/20 (m3)") log("END: 70/20 (m3)")
# via3 # via3
log("START: 70/44 (via3)") log("START: 70/44 (via3)")
if backend_flow = AL if backend_flow == AL
if SEAL if SEAL
ringVIA3 = via3.drc(with_holes > 0) ringVIA3 = via3.drc(with_holes > 0)
rectVIA3 = via3.not(ringVIA3) rectVIA3 = via3.not(ringVIA3)
@@ -1307,7 +1447,7 @@ if BEOL
via3_not_mt via3_not_mt
.edges .edges
.without_length(nil, 0.2 + 1.dbu) .without_length(nil, 0.2 + 1.dbu)
.output("via3.1_b", "via3.1_b : maximum length of via3 : 0.2um") .output("MR_via3.LEN.1", "MR_via3.LEN.1 : maximum length of via3 : 0.2um")
via3.space(0.2, euclidian).output( via3.space(0.2, euclidian).output(
"via3.2", "via3.2",
@@ -1336,7 +1476,7 @@ if BEOL
# m4 # m4
log("START: 71/20 (m4)") log("START: 71/20 (m4)")
m4.width(0.3, euclidian).output("m4.1", "m4.1 : min. m4 width : 0.3um") 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 huge_m4 = m4.sized(-1.5).sized(1.5).snap(0.005) & m4
non_huge_m4 = m4.edges - huge_m4 non_huge_m4 = m4.edges - huge_m4
@@ -1347,29 +1487,29 @@ if BEOL
"m4.2 : min. m4 spacing : 0.3um" "m4.2 : min. m4 spacing : 0.3um"
) )
m4.with_area(0..0.240).output("m4.4a", "m4.4a : min. m4 area : 0.240um²") 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.separation(non_huge_m4, 0.4, euclidian) +
huge_m4.space(0.4, euclidian) huge_m4.space(0.4, euclidian)
).output("m4.5ab", "m4.5ab : min. 3um.m4 spacing m4 : 0.4um") ).output("MRW_met4.SP.2", "MRW_met4.SP.2 : min. 3um.m4 spacing m4 : 0.4um")
if FLOATING_MET if FLOATING_MET
m4.not_interacting(via3.or(via4)).output( m4.not_interacting(via3.or(via4)).output(
"m4.x", "m4.x",
"floating met3, must interact with via3 or via4" "floating met3, must interact with via3 or via4"
) )
end end
if backend_flow = AL if backend_flow == AL
m4.enclosing(via3, 0.065, euclidian).output( m4.enclosing(via3, 0.065, euclidian).output(
"m4.3", "m4.3",
"m4.3 : min. m4 enclosure of via3 : 0.065um" "m4.3 : min. m4 enclosure of via3 : 0.065um"
) )
via3.not(m4).output("m4.3_a", "m4.3_a : via3 must be enclosed by met4") via3.not(m4).output("MR_met4.CON.1", "MR_met4.CON.1 : via3 must be enclosed by met4")
end end
m4 m4
.holes .holes
.with_area(0..0.2) .with_area(0..0.2)
.output("m4.7", "m4.7 : min. m4 holes area : 0.2um²") .output("MR_met4.AR.2", "MR_met4.AR.2 : min. m4 holes area : 0.2um²")
log("END: 71/20 (m4)") log("END: 71/20 (m4)")
# via4 # via4
@@ -1398,14 +1538,14 @@ if BEOL
via4 via4
.space(0.8, euclidian) .space(0.8, euclidian)
.polygons .polygons
.output("via4.2", "via4.2 : min. via4 spacing : 0.8um") .output("MR_via4.SP.1", "MR_via4.SP.1 : min. via4 spacing : 0.8um")
if SEAL if SEAL
ringVIA4.width(0.8, euclidian).output( ringVIA4.width(0.8, euclidian).output(
"via4.3", "via4.3",
"via4.3 : min. width of ring-shaped via4 : 0.8um" "via4.3 : min. width of ring-shaped via4 : 0.8um"
) )
ringVIA4.drc(width >= 0.805).output( ringVIA4.drc(width(euclidian) >= 0.805).output(
"via4.3_a", "via4.3_a",
"via4.3_a : max. width of ring-shaped via4 : 0.805um" "via4.3_a : max. width of ring-shaped via4 : 0.805um"
) )
@@ -1419,31 +1559,31 @@ if BEOL
"via4.4", "via4.4",
"via4.4 : min. m4 enclosure of via4 : 0.19um" "via4.4 : min. m4 enclosure of via4 : 0.19um"
) )
rectVIA4.not(m4).output("via4.4_a", "via4.4_a : m4 must enclose all via4") rectVIA4.not(m4).output("MR_via4.CON.1", "MR_via4.CON.1 : m4 must enclose all via4")
log("END: 71/44 (via4)") log("END: 71/44 (via4)")
# m5 # m5
log("START: 72/20 (m5)") log("START: 72/20 (m5)")
m5.width(1.6, euclidian).output("m5.1", "m5.1 : min. m5 width : 1.6um") 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("m5.2", "m5.2 : min. m5 spacing : 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.enclosing(via4, 0.31, euclidian).output(
"m5.3", "m5.3",
"m5.3 : min. m5 enclosure of via4 : 0.31um" "m5.3 : min. m5 enclosure of via4 : 0.31um"
) )
via4.not(m5).output("m5.3_a", "m5.3_a : via must be enclosed by m5") via4.not(m5).output("MR_met5.CON.1", "MR_met5.CON.1 : via must be enclosed by m5")
if FLOATING_MET if FLOATING_MET
m5.not_interacting(via4).output( m5.not_interacting(via4).output(
"m5.x", "m5.x",
"floating met5, must interact with via4" "floating met5, must interact with via4"
) )
end end
m5.with_area(0..4.0).output("m5.4", "m5.4 : min. m5 area : 4.0um²") m5.with_area(0..4.0).output("MR_met5.AR.1", "MR_met5.AR.1 : min. m5 area : 4.0um²")
m5 m5
.holes .holes
.with_area(0..0.14) .with_area(0..0.14)
.output("m5.7", "m5.7 : min. m5 holes area : 0.14um²") .output("MR_met5.AR.2", "MR_met5.AR.2 : min. m5 holes area : 0.14um²")
log("END: 72/20 (m5)") log("END: 72/20 (m5)")
# pad # pad