Initial klayout DRC/LVS options

This commit is contained in:
mrg 2021-08-03 14:41:09 -07:00
parent aa0e221863
commit c117238fa7
7 changed files with 712 additions and 14 deletions

View File

@ -32,14 +32,16 @@ if not OPTS.check_lvsdrc:
# OPTS.magic_exe = None
else:
debug.info(1, "Finding DRC/LVS/PEX tools.")
OPTS.drc_exe = get_tool("DRC", ["calibre", "assura", "magic"], drc_name)
OPTS.lvs_exe = get_tool("LVS", ["calibre", "assura", "netgen"], lvs_name)
OPTS.pex_exe = get_tool("PEX", ["calibre", "magic"], pex_name)
OPTS.drc_exe = get_tool("DRC", ["klayout", "calibre", "assura", "magic"], drc_name)
OPTS.lvs_exe = get_tool("LVS", ["klayout", "calibre", "assura", "netgen"], lvs_name)
OPTS.pex_exe = get_tool("PEX", ["klayout", "calibre", "magic"], pex_name)
# if OPTS.tech_name == "sky130":
# OPTS.magic_exe = get_tool("GDS", ["magic"])
if not OPTS.drc_exe:
from .none import run_drc, print_drc_stats, write_drc_script
elif "klayout"==OPTS.drc_exe[0]:
from .klayout import run_drc, print_drc_stats, write_drc_script
elif "calibre"==OPTS.drc_exe[0]:
from .calibre import run_drc, print_drc_stats, write_drc_script
elif "assura"==OPTS.drc_exe[0]:
@ -52,6 +54,8 @@ else:
if not OPTS.lvs_exe:
from .none import run_lvs, print_lvs_stats, write_lvs_script
elif "klayout"==OPTS.lvs_exe[0]:
from .klayout import run_lvs, print_lvs_stats, write_lvs_script
elif "calibre"==OPTS.lvs_exe[0]:
from .calibre import run_lvs, print_lvs_stats, write_lvs_script
elif "assura"==OPTS.lvs_exe[0]:
@ -65,6 +69,8 @@ else:
if not OPTS.pex_exe:
from .none import run_pex, print_pex_stats
elif "klayout"==OPTS.pex_exe[0]:
from .klayout import run_pex, print_pex_stats
elif "calibre"==OPTS.pex_exe[0]:
from .calibre import run_pex, print_pex_stats
elif "magic"==OPTS.pex_exe[0]:
@ -78,4 +84,3 @@ else:
# from .magic import filter_gds
# else:
# debug.warning("Did not find Magic.")

View File

@ -0,0 +1,430 @@
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description/>
<version/>
<category>drc</category>
<prolog/>
<epilog/>
<doc/>
<autorun>false</autorun>
<autorun-early>false</autorun-early>
<shortcut/>
<show-in-menu>true</show-in-menu>
<group-name>drc_scripts</group-name>
<menu-path>tools_menu.drc.end</menu-path>
<interpreter>dsl</interpreter>
<dsl-interpreter-name>drc-dsl-xml</dsl-interpreter-name>
<text>
#
# DRC for FreePDK45 according to :
# https://www.eda.ncsu.edu/wiki/FreePDK45:RuleDevel
# https://www.eda.ncsu.edu/wiki/FreePDK45:Contents
#
##########################################################################################
tstart = Time.now
# optionnal for a batch launch : klayout -b r drc_FreePDK45.lydrc -rd input=my_layout.gds -rd topcell=your_topcell -rd output=drc_FreePDK45.lyrdb
if $input
if $topcell
source($input,$topcell)
else
source($input)
end
end
if $output
report("FreePDK45 DRC runset", $output)
else
report("FreePDK45 DRC runset", "FreePDK45_DRC.lyrdb")
end
# DRC test to run or not
###############
OFFGRID = true
ANTENNA = true
DRC = true
# KLAYOUT setup
########################
# Use a tile size of 1mm
# tiles(100.um)
# Use a tile border of 10 micron:
# tile_borders(1.um)
# no_borders
# Hierachical
deep
# Use 4 CPU cores
threads(4)
verbose(true)
# layers definitions
########################
active = polygons(1, 0)
pwell = polygons(2, 0)
nwell = polygons(3, 0)
nplus = polygons(4, 0)
pplus = polygons(5, 0)
vtg = polygons(6, 0)
vth = polygons(7, 0)
thkox = polygons(8, 0)
poly = polygons(9, 0)
cont = polygons(10, 0)
metal1 = polygons(11, 0)
via1 = polygons(12, 0)
metal2 = polygons(13, 0)
via2 = polygons(14, 0)
metal3 = polygons(15, 0)
via3 = polygons(16, 0)
metal4 = polygons(17, 0)
via4 = polygons(18, 0)
metal5 = polygons(19, 0)
via5 = polygons(20, 0)
metal6 = polygons(21, 0)
via6 = polygons(22, 0)
metal7 = polygons(23, 0)
via7 = polygons(24, 0)
metal8 = polygons(25, 0)
via8 = polygons(26, 0)
metal9 = polygons(27, 0)
via9 = polygons(28, 0)
metal10 = polygons(29, 0)
# Computed layers
well = nwell.or(pwell)
gate = poly &amp; active
implant = nplus.or(pplus)
if DRC
# DRC section
########################
info("DRC section")
# splits a layer classes with increasing min dimensions
def classify_by_width(layer, *dimensions)
dimensions.collect { |d| layer = layer.sized(-0.5 * (d - 1.dbu)).sized(0.5 * (d - 1.dbu)) }
end
# Wells
nwell.and(pwell).output("WELL.1", "WELL.1 : nwell/pwell must not overlap")
# the rule "WELL.2 : Minimum spacing of well at different potential : 225nm" was not coded : see : https://www.klayout.de/forum/discussion/comment/6021
nwell.space(135.nm, euclidian).output("WELL.3", "WELL.3 : Minimum spacing of nwell at same potential : 135nm")
pwell.space(135.nm, euclidian).output("WELL.3", "WELL.3 : Minimum spacing of pwell at same potential : 135nm")
well.separation(well, 200.nm, euclidian).output("WELL.4", "WELL.4 : Minimum width of nwell/pwell : 200nm")
vtg.not(well).output("VT.1","VT.1 : Vtg adjust layers must coincide with well")
vth.not(well).output("VT.1","VT.1 : Vth adjust layers must coincide with well")
# Poly
poly.width(50.nm, euclidian).output("POLY.1", "POLY.1 : Minimum width of poly : 50nm")
poly_sep_active = poly.separation(active, 140.nm, projection)
if poly_sep_active.polygons?
poly_sep_active.polygons.without_area(0).output("POLY.2", "POLY.2 : Minimum spacing of poly AND active: 140nm")
end
poly_sep_active.forget
poly.enclosing(gate, 55.nm, projection).polygons.without_area(0).output("POLY.3", "POLY.3 : Minimum poly extension beyond active : 55nm")
active.enclosing(gate, 70.nm, projection).polygons.without_area(0).output("POLY.4", "POLY.4 : Minimum enclosure of active around gate : 70nm")
poly.not(active).separation(active, 50.nm, projection).polygons.without_area(0).output("POLY.5", "POLY.5 : Minimum spacing of field poly to active: 50nm")
poly.space(75.nm, euclidian).output("POLY.6", "POLY.6 : Minimum spacing of field poly: 75nm")
# Active
active.width(90.nm, euclidian).output("ACTIVE.1", "ACTIVE.1 : Minimum width of active : 90nm")
active.space(80.nm, euclidian).output("ACTIVE.2", "ACTIVE.2 : Minimum spacing of active : 80nm")
well.enclosing(active, 55.nm, euclidian).output("ACTIVE.3", "ACTIVE.3 : Minimum enclosure/spacing of nwell/pwell to active: 55nm")
active.not(well).output("ACTIVE.4", "ACTIVE.4 : active must be inside nwell or pwell")
# Implant
implant.separation(gate, 70.nm, projection).polygons.without_area(0).output("IMPLANT.1", "IMPLANT.1 : Minimum spacing of nimplant/ pimplant to channel : 70nm")
implant.separation(cont, 25.nm, projection).polygons.without_area(0).output("IMPLANT.2", "IMPLANT.1 : Minimum spacing of nimplant/ pimplant to contact : 25nm")
implant.width(45.nm, euclidian).output("IMPLANT.3", "IMPLANT.3 : Minimum width of nimplant/ pimplant : 45nm")
implant.space(45.nm, euclidian).output("IMPLANT.4", "IMPLANT.4 : Minimum spacing of nimplant/ pimplant : 45nm")
nplus.and(pplus).output("IMPLANT.5", "IMPLANT.5 : Nimplant and pimplant must not overlap")
implant.forget
# Contact
cont.edges.without_length(65.nm).output("CONTACT.1", "CONTACT.1 : Minimum/Maximum width of contact : 65nm")
cont.space(75.nm, euclidian).output("CONTACT.2", "CONTACT.2 : Minimum spacing of contact : 75nm")
cont.not(active).not(poly).not(metal1).output("CONTACT.3", "CONTACT.3 : contact must be inside active or poly or metal1")
active.enclosing(cont, 5.nm, euclidian).output("CONTACT.4", "CONTACT.4 : Minimum enclosure of active around contact : 5nm")
poly.enclosing(cont, 5.nm, euclidian).output("CONTACT.5", "CONTACT.5 : Minimum enclosure of poly around contact : 5nm")
cont.separation(poly, 35.nm, euclidian).output("CONTACT.6", "CONTACT.6 : Minimum spacing of contact and poly : 35nm")
# Metal1
metal1.width(65.nm, euclidian).output("METAL1.1", "METAL1.1 : Minimum width of metal1 : 65nm")
metal1.space(65.nm, euclidian).output("METAL1.2", "METAL1.2 : Minimum spacing of metal1 : 65nm")
cont_edges_with_less_enclosure = metal1.enclosing(cont, 35.nm, projection).second_edges
error_corners = cont_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu)
cont_edges_with_less_enclosure.forget
cont.interacting(error_corners.polygons(1.dbu)).output("METAL1.3", "METAL1.3 : Minimum enclosure around contact on two opposite sides : 35nm")
error_corners.forget
via1_edges_with_less_enclosure = metal1.enclosing(via1, 35.nm, projection).second_edges
error_corners = via1_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu)
via1_edges_with_less_enclosure.forget
via1.interacting(error_corners.polygons(1.dbu)).output("METAL1.4", "METAL1.4 : Minimum enclosure around via1 on two opposite sides : 35nm")
error_corners.forget
metal1_gt90, metal1_gt270, metal1_gt500, metal1_gt900, metal1_gt1500 = classify_by_width(metal1, 90.nm, 270.nm, 500.nm, 900.nm, 1500.nm)
metal1_gt90.edges.with_length(300.nm,nil).space(90.nm,euclidian).output("METAL1.5", "METAL1.5 : Minimum spacing of metal1 wider than 90 nm and longer than 300 nm : 90nm")
metal1_gt270.edges.with_length(900.nm,nil).space(270.nm,euclidian).output("METAL1.6", "METAL1.6 : Minimum spacing of metal1 wider than 270 nm and longer than 900 nm : 270nm")
metal1_gt500.edges.with_length(1.8.um,nil).space(500.nm,euclidian).output("METAL1.7", "METAL1.7 : Minimum spacing of metal1 wider than 500 nm and longer than 1.8 um : 500nm")
metal1_gt900.edges.with_length(2.7.um,nil).space(900.nm,euclidian).output("METAL1.8", "METAL1.8 : Minimum spacing of metal1 wider than 900 nm and longer than 2.7 um : 900nm")
metal1_gt1500.edges.with_length(4.um,nil).space(1500.nm,euclidian).output("METAL1.9", "METAL1.9 : Minimum spacing of metal1 wider than 1500 nm and longer than 4.0 um : 1500nm")
[ metal1_gt90, metal1_gt270, metal1_gt500, metal1_gt900, metal1_gt1500 ].each { |l| l.forget }
# Via1
via1.edges.without_length(65.nm).output("VIA1.1", "VIA1.1 : Minimum/Maximum width of via1 : 65nm")
via1.space(75.nm, euclidian).output("VIA1.2", "VIA1.2 : Minimum spacing of via1 : 75nm")
via1.not(metal1).output("VIA1.3", "VIA1.3 : via1 must be inside metal1")
via1.not(metal2).output("VIA1.4", "VIA1.4 : via1 must be inside metal2")
# metal2
metal2.width(70.nm, euclidian).output("METAL2.1", "METAL2.1 : Minimum width of intermediate metal2 : 70nm")
metal2.space(70.nm, euclidian).output("METAL2.2", "METAL2.2 : Minimum spacing of intermediate metal2 : 70nm")
via1_edges_with_less_enclosure = metal2.enclosing(via1, 35.nm, projection).second_edges
error_corners = via1_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu)
via1_edges_with_less_enclosure.forget
via1.interacting(error_corners.polygons(1.dbu)).output("METAL2.3", "METAL2.3 : Minimum enclosure around via1 on two opposite sides : 35nm")
error_corners.forget
via2_edges_with_less_enclosure = metal2.enclosing(via2, 35.nm, projection).second_edges
error_corners = via2_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu)
via2_edges_with_less_enclosure.forget
via2.interacting(error_corners.polygons(1.dbu)).output("METAL2.4", "METAL2.4 : Minimum enclosure around via2 on two opposite sides : 35nm")
error_corners.forget
metal2_gt90, metal2_gt270, metal2_gt500, metal2_gt900, metal2_gt1500 = classify_by_width(metal2, 90.nm, 270.nm, 500.nm, 900.nm, 1500.nm)
metal2_gt90.edges.with_length(300.nm,nil).space(90.nm,euclidian).output("METAL2.5", "METAL2.5 : Minimum spacing of intermediate metal2 wider than 90 nm and longer than 300 nm : 90nm")
metal2_gt270.edges.with_length(900.nm,nil).space(270.nm,euclidian).output("METAL2.6", "METAL2.6 : Minimum spacing of intermediate metal2 wider than 270 nm and longer than 900 nm : 270nm")
metal2_gt500.edges.with_length(1.8.um,nil).space(500.nm,euclidian).output("METAL2.7", "METAL2.7 : Minimum spacing of intermediate metal2 wider than 500 nm and longer than 1.8 um : 500nm")
metal2_gt900.edges.with_length(2.7.um,nil).space(900.nm,euclidian).output("METAL2.8", "METAL2.8 : Minimum spacing of intermediate metal2 wider than 900 nm and longer than 2.7 um : 900nm")
metal2_gt1500.edges.with_length(4.um,nil).space(1500.nm,euclidian).output("METAL2.9", "METAL2.9 : Minimum spacing of intermediate metal2 wider than 1500 nm and longer than 4.0 um : 1500nm")
[ metal2_gt90, metal2_gt270, metal2_gt500, metal2_gt900, metal2_gt1500 ].each { |l| l.forget }
# via2
via2.edges.without_length(70.nm).output("VIA2.1", "VIA2.1 : Minimum/Maximum width of via2 : 70nm")
via2.space(85.nm, euclidian).output("VIA2.2", "VIA2.2 : Minimum spacing of via2 : 85nm")
via2.not(metal2).output("VIA2.3", "VIA2.3 : via2 must be inside metal2")
via2.not(metal3).output("VIA2.4", "VIA2.4 : via2 must be inside metal3")
# metal3
metal3.width(70.nm, euclidian).output("METAL3.1", "METAL3.1 : Minimum width of intermediate metal3 : 70nm")
metal3.space(70.nm, euclidian).output("METAL3.2", "METAL3.2 : Minimum spacing of intermediate metal3 : 70nm")
via2_edges_with_less_enclosure = metal3.enclosing(via2, 35.nm, projection).second_edges
error_corners = via2_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu)
via2_edges_with_less_enclosure.forget
via2.interacting(error_corners.polygons(1.dbu)).output("METAL3.3", "METAL3.3 : Minimum enclosure around via2 on two opposite sides : 35nm")
error_corners.forget
via3_edges_with_less_enclosure = metal3.enclosing(via3, 35.nm, projection).second_edges
error_corners = via3_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu)
via3_edges_with_less_enclosure.forget
via3.interacting(error_corners.polygons(1.dbu)).output("METAL3.4", "METAL3.4 : Minimum enclosure around via3 on two opposite sides : 35nm")
error_corners.forget
metal3_gt90, metal3_gt270, metal3_gt500, metal3_gt900, metal3_gt1500 = classify_by_width(metal3, 90.nm, 270.nm, 500.nm, 900.nm, 1500.nm)
metal3_gt90.edges.with_length(300.nm,nil).space(90.nm,euclidian).output("METAL3.5", "METAL3.5 : Minimum spacing of intermediate metal3 wider than 90 nm and longer than 300 nm : 90nm")
metal3_gt270.edges.with_length(900.nm,nil).space(270.nm,euclidian).output("METAL3.6", "METAL3.6 : Minimum spacing of intermediate metal3 wider than 270 nm and longer than 900 nm : 270nm")
metal3_gt500.edges.with_length(1.8.um,nil).space(500.nm,euclidian).output("METAL3.7", "METAL3.7 : Minimum spacing of intermediate metal3 wider than 500 nm and longer than 1.8 um : 500nm")
metal3_gt900.edges.with_length(2.7.um,nil).space(900.nm,euclidian).output("METAL3.8", "METAL3.8 : Minimum spacing of intermediate metal3 wider than 900 nm and longer than 2.7 um : 900nm")
metal3_gt1500.edges.with_length(4.um,nil).space(1500.nm,euclidian).output("METAL3.9", "METAL3.9 : Minimum spacing of intermediate metal3 wider than 1500 nm and longer than 4.0 um : 1500nm")
[ metal3_gt90, metal3_gt270, metal3_gt500, metal3_gt900, metal3_gt1500 ].each { |l| l.forget }
# via3
via3.edges.without_length(70.nm).output("VIA3.1", "VIA3.1 : Minimum/Maximum width of via3 : 70nm")
via3.space(85.nm, euclidian).output("VIA3.2", "VIA3.2 : Minimum spacing of via3 : 85nm")
via3.not(metal3).output("VIA3.3", "VIA3.3 : via3 must be inside metal3")
via3.not(metal4).output("VIA3.4", "VIA3.4 : via3 must be inside metal4")
# metal4
metal4.width(140.nm, euclidian).output("METAL4.1", "METAL4.1 : Minimum width of semi-global metal4 : 140nm")
metal4.space(140.nm, euclidian).output("METAL4.2", "METAL4.2 : Minimum spacing of semi-global metal4 : 140nm")
metal4_gt270, metal4_gt500, metal4_gt900 = classify_by_width(metal4, 270.nm, 500.nm, 900.nm)
metal4_gt270.edges.with_length(900.nm,nil).space(270.nm,euclidian).output("METAL4.6", "METAL4.6 : Minimum spacing of semi-global metal4 wider than 270 nm and longer than 900 nm : 270nm")
metal4_gt500.edges.with_length(1.8.um,nil).space(500.nm,euclidian).output("METAL4.7", "METAL4.7 : Minimum spacing of semi-global metal4 wider than 500 nm and longer than 1.8 um : 500nm")
metal4_gt900.edges.with_length(2.7.um,nil).space(900.nm,euclidian).output("METAL4.8", "METAL4.8 : Minimum spacing of semi-global meta4l wider than 900 nm and longer than 2.7 um : 900nm")
[ metal4_gt270, metal4_gt500, metal4_gt900 ].each { |l| l.forget }
# via4
via4.edges.without_length(140.nm).output("VIA4.1", "VIA4.1 : Minimum/Maximum width of via4 : 140nm")
via4.space(160.nm, euclidian).output("VIA4.2", "VIA4.2 : Minimum spacing of via4 : 160nm")
via4.not(metal4).output("VIA4.3", "VIA4.3 : via4 must be inside metal4")
via4.not(metal5).output("VIA4.4", "VIA4.4 : via4 must be inside metal5")
# metal5
metal5.width(140.nm, euclidian).output("METAL5.1", "METAL5.1 : Minimum width of semi-global metal5 : 140nm")
metal5.space(140.nm, euclidian).output("METAL5.2", "METAL5.2 : Minimum spacing of semi-global metal5 : 140nm")
metal5_gt270, metal5_gt500, metal5_gt900 = classify_by_width(metal5, 270.nm, 500.nm, 900.nm)
metal5_gt270.edges.with_length(900.nm,nil).space(270.nm,euclidian).output("METAL5.6", "METAL5.6 : Minimum spacing of semi-global metal5 wider than 270 nm and longer than 900 nm : 270nm")
metal5_gt500.edges.with_length(1.8.um,nil).space(500.nm,euclidian).output("METAL5.7", "METAL5.7 : Minimum spacing of semi-global metal5 wider than 500 nm and longer than 1.8 um : 500nm")
metal5_gt900.edges.with_length(2.7.um,nil).space(900.nm,euclidian).output("METAL5.8", "METAL5.8 : Minimum spacing of semi-global meta5l wider than 900 nm and longer than 2.7 um : 900nm")
[ metal5_gt270, metal5_gt500, metal5_gt900 ].each { |l| l.forget }
# via5
via5.edges.without_length(140.nm).output("VIA5.1", "VIA5.1 : Minimum/Maximum width of via5 : 140nm")
via5.space(160.nm, euclidian).output("VIA5.2", "VIA5.2 : Minimum spacing of via5 : 160nm")
via5.not(metal5).output("VIA5.3", "VIA5.3 : via5 must be inside metal5")
via5.not(metal6).output("VIA5.4", "VIA5.4 : via5 must be inside metal6")
# metal6
metal6.width(140.nm, euclidian).output("METAL6.1", "METAL6.1 : Minimum width of semi-global metal6 : 140nm")
metal6.space(140.nm, euclidian).output("METAL6.2", "METAL6.2 : Minimum spacing of semi-global metal6 : 140nm")
metal6_gt270, metal6_gt500, metal6_gt900 = classify_by_width(metal6, 270.nm, 500.nm, 900.nm)
metal6_gt270.edges.with_length(900.nm,nil).space(270.nm,euclidian).output("METAL6.6", "METAL6.6 : Minimum spacing of semi-global metal6 wider than 270 nm and longer than 900 nm : 270nm")
metal6_gt500.edges.with_length(1.8.um,nil).space(500.nm,euclidian).output("METAL6.7", "METAL6.7 : Minimum spacing of semi-global metal6 wider than 500 nm and longer than 1.8 um : 500nm")
metal6_gt900.edges.with_length(2.7.um,nil).space(900.nm,euclidian).output("METAL6.8", "METAL6.8 : Minimum spacing of semi-global metal6 wider than 900 nm and longer than 2.7 um : 900nm")
[ metal6_gt270, metal6_gt500, metal6_gt900 ].each { |l| l.forget }
# via6
via6.edges.without_length(140.nm).output("VIA6.1", "VIA6.1 : Minimum/Maximum width of via6 : 140nm")
via6.space(160.nm, euclidian).output("VIA6.2", "VIA6.2 : Minimum spacing of via6 : 160nm")
via6.not(metal6).output("VIA6.3", "VIA6.3 : via6 must be inside metal6")
via6.not(metal7).output("VIA6.4", "VIA6.4 : via6 must be inside metal7")
# metal7
metal7.width(400.nm, euclidian).output("METAL7.1", "METAL7.1 : Minimum width of thin global metal7 : 400nm")
metal7.space(400.nm, euclidian).output("METAL7.2", "METAL7.2 : Minimum spacing of thin global metal7 : 400nm")
metal7_gt500, metal7_gt900, metal7_gt1500 = classify_by_width(metal7, 500.nm, 900.nm, 1500.nm)
metal7_gt500.edges.with_length(1.8.um,nil).space(500.nm,euclidian).output("METAL7.7", "METAL7.7 : Minimum spacing of thin global metal7 wider than 500 nm and longer than 1.8 um : 500nm")
metal7_gt900.edges.with_length(2.7.um,nil).space(900.nm,euclidian).output("METAL7.8", "METAL7.8 : Minimum spacing of thin global metal7 wider than 900 nm and longer than 2.7 um : 900nm")
metal7_gt1500.edges.with_length(4.um,nil).space(1500.nm,euclidian).output("METAL7.9", "METAL7.9 : Minimum spacing of thin global meta7l wider than 1500 nm and longer than 4.0 um : 1500nm")
[ metal7_gt500, metal7_gt900, metal7_gt1500 ].each { |l| l.forget }
# via7
via7.edges.without_length(400.nm).output("VIA6.1", "VIA6.1 : Minimum/Maximum width of via7 : 400nm")
via7.space(440.nm, euclidian).output("VIA6.2", "VIA6.2 : Minimum spacing of via7 : 440nm")
via7.not(metal7).output("VIA7.3", "VIA7.3 : via7 must be inside metal7")
via7.not(metal8).output("VIA7.4", "VIA7.4 : via7 must be inside metal8")
# metal8
metal8.width(400.nm, euclidian).output("METAL8.1", "METAL8.1 : Minimum width of thin global metal8 : 400nm")
metal8.space(400.nm, euclidian).output("METAL8.2", "METAL8.2 : Minimum spacing of thin global metal8 : 400nm")
metal8_gt500, metal8_gt900, metal8_gt1500 = classify_by_width(metal8, 500.nm, 900.nm, 1500.nm)
metal8_gt500.edges.with_length(1.8.um,nil).space(500.nm,euclidian).output("METAL8.7", "METAL8.7 : Minimum spacing of thin global metal8 wider than 500 nm and longer than 1.8 um : 500nm")
metal8_gt900.edges.with_length(2.7.um,nil).space(900.nm,euclidian).output("METAL8.8", "METAL8.8 : Minimum spacing of thin global metal8 wider than 900 nm and longer than 2.7 um : 900nm")
metal8_gt1500.edges.with_length(4.um,nil).space(1500.nm,euclidian).output("METAL8.9", "METAL8.9 : Minimum spacing of thin global metal8 wider than 1500 nm and longer than 4.0 um : 1500nm")
[ metal8_gt500, metal8_gt900, metal8_gt1500 ].each { |l| l.forget }
# via8
via8.edges.without_length(400.nm).output("VIA8.1", "VIA8.1 : Minimum/Maximum width of via8 : 400nm")
via8.space(440.nm, euclidian).output("VIA8.2", "VIA8.2 : Minimum spacing of via8 : 440nm")
via8.not(metal8).output("VIA8.3", "VIA8.3 : via8 must be inside metal8")
via8.not(metal9).output("VIA8.4", "VIA8.4 : via8 must be inside metal9")
# metal9
metal9.width(800.nm, euclidian).output("METAL9.1", "METAL9.1 : Minimum width of global metal9 : 800nm")
metal9.space(800.nm, euclidian).output("METAL9.2", "METAL9.2 : Minimum spacing of global metal9 : 800nm")
metal9_gt500, metal9_gt900, metal9_gt1500 = classify_by_width(metal9, 500.nm, 900.nm, 1500.nm)
metal9_gt500.edges.with_length(1.8.um,nil).space(500.nm,euclidian).output("METAL9.7", "METAL9.7 : Minimum spacing of global metal9 wider than 500 nm and longer than 1.8 um : 500nm")
metal9_gt900.edges.with_length(2.7.um,nil).space(900.nm,euclidian).output("METAL9.8", "METAL9.8 : Minimum spacing of global metal9 wider than 900 nm and longer than 2.7 um : 900nm")
metal9_gt1500.edges.with_length(4.um,nil).space(1500.nm,euclidian).output("METAL9.9", "METAL9.9 : Minimum spacing of global metal9 wider than 1500 nm and longer than 4.0 um : 1500nm")
[ metal9_gt500, metal9_gt900, metal9_gt1500 ].each { |l| l.forget }
# via9
via9.edges.without_length(800.nm).output("VIA9.1", "VIA9.1 : Minimum/Maximum width of via9 : 800nm")
via9.space(880.nm, euclidian).output("VIA9.2", "VIA9.2 : Minimum spacing of via9 : 880nm")
via9.not(metal9).output("VIA9.3", "VIA9.3 : via9 must be inside metal9")
via9.not(metal10).output("VIA9.4", "VIA9.4 : via9 must be inside metal10")
# metal10
metal10.width(800.nm, euclidian).output("METAL10.1", "METAL10.1 : Minimum width of global metal10 : 800nm")
metal10.space(800.nm, euclidian).output("METAL10.2", "METAL10.2 : Minimum spacing of global metal10 : 800nm")
metal10_gt500, metal10_gt900, metal10_gt1500 = classify_by_width(metal10, 500.nm, 900.nm, 1500.nm)
metal10_gt500.edges.with_length(1.8.um,nil).space(500.nm,euclidian).output("METAL10.7", "METAL10.7 : Minimum spacing of global metal10 wider than 500 nm and longer than 1.8 um : 500nm")
metal10_gt900.edges.with_length(2.7.um,nil).space(900.nm,euclidian).output("METAL10.8", "METAL10.8 : Minimum spacing of global metal10 wider than 900 nm and longer than 2.7 um : 900nm")
metal10_gt1500.edges.with_length(4.um,nil).space(1500.nm,euclidian).output("METAL10.9", "METAL10.9 : Minimum spacing of global metal10 wider than 1500 nm and longer than 4.0 um : 1500nm")
[ metal10_gt500, metal10_gt900, metal10_gt1500 ].each { |l| l.forget }
end
# ONGRID also defined in :
# https://www.eda.ncsu.edu/wiki/FreePDK45:Manufacturing_Grid
###########################################
if OFFGRID
info("GRID section")
grid = 2.5.nm
all_drawing = [ :well, :active, :vtg, :vth, :pplus, :nplus, :poly, :thkox, :cont, :metal1, :via1, :metal2, :via2, :metal3, :via3, :metal4, :via4, :metal5, :via5, :metal6, :via6, :metal7, :via7, :metal8, :via8, :metal9, :via9, :metal10 ]
all_drawing.each do |dwg|
# a Ruby idiom to get the value of a variable whose name is in "dwg" (as symbol)
layer = binding.local_variable_get(dwg)
r_grid = layer.ongrid(grid).polygons(10.nm)
r_grid.output("GRID: vertexes on layer #{dwg} not on grid of #{'%.12g' % grid}")
end
end
# ANTENNA checks
################
if ANTENNA
info("ANTENNA section")
diode = nplus &amp; active - nwell # diode recognition layer
# build connction of poly+gate to metal1
connect(gate, poly)
connect(poly, cont)
connect(diode, cont)
connect(cont, metal1)
antenna_check(gate, metal1, 300.0, diode).output("METAL1_ANTENNA", "METAL1_ANTENNA : Ratio of Maximum Allowed (Field poly area or Metal Layer Area) to transistor gate area : 300:1")
# build connction of poly+gate to metal2
connect(metal1, via1)
connect(via1, metal2)
antenna_check(gate, metal2, 300.0, diode).output("METAL2_ANTENNA", "METAL2_ANTENNA : Ratio of Maximum Allowed (Field poly area or Metal Layer Area) to transistor gate area : 300:1")
# build connction of poly+gate to metal3
connect(metal2, via2)
connect(via2, metal3)
antenna_check(gate, metal3, 300.0, diode).output("METAL3_ANTENNA", "METAL3_ANTENNA : Ratio of Maximum Allowed (Field poly area or Metal Layer Area) to transistor gate area : 300:1")
# build connction of poly+gate to metal4
connect(metal3, via3)
connect(via3, metal4)
antenna_check(gate, metal4, 300.0, diode).output("METAL4_ANTENNA", "METAL4_ANTENNA : Ratio of Maximum Allowed (Field poly area or Metal Layer Area) to transistor gate area : 300:1")
# build connction of poly+gate to metal5
connect(metal4, via4)
connect(via4, metal5)
antenna_check(gate, metal5, 300.0, diode).output("METAL5_ANTENNA", "METAL5_ANTENNA : Ratio of Maximum Allowed (Field poly area or Metal Layer Area) to transistor gate area : 300:1")
# build connction of poly+gate to metal6
connect(metal5, via5)
connect(via5, metal6)
antenna_check(gate, metal6, 300.0, diode).output("METAL6_ANTENNA", "METAL6_ANTENNA : Ratio of Maximum Allowed (Field poly area or Metal Layer Area) to transistor gate area : 300:1")
# build connction of poly+gate to metal7
connect(metal6, via6)
connect(via6, metal7)
antenna_check(gate, metal7, 300.0, diode).output("METAL7_ANTENNA", "METAL7_ANTENNA : Ratio of Maximum Allowed (Field poly area or Metal Layer Area) to transistor gate area : 300:1")
# build connction of poly+gate to metal8
connect(metal7, via7)
connect(via7, metal8)
antenna_check(gate, metal8, 300.0, diode).output("METAL8_ANTENNA", "METAL8_ANTENNA : Ratio of Maximum Allowed (Field poly area or Metal Layer Area) to transistor gate area : 300:1")
# build connction of poly+gate to metal9
connect(metal8, via8)
connect(via8, metal9)
antenna_check(gate, metal9, 300.0, diode).output("METAL9_ANTENNA", "METAL9_ANTENNA : Ratio of Maximum Allowed (Field poly area or Metal Layer Area) to transistor gate area : 300:1")
# build connction of poly+gate to metal10
connect(metal9, via9)
connect(via9, metal10)
antenna_check(gate, metal10, 300.0, diode).output("METAL10_ANTENNA", "METAL10_ANTENNA : Ratio of Maximum Allowed (Field poly area or Metal Layer Area) to transistor gate area : 300:1")
end
# time spent for the DRC
time = Time.now
hours = ((time - tstart)/3600).to_i
minutes = ((time - tstart)/60 - hours * 60).to_i
seconds = ((time - tstart) - (minutes * 60 + hours * 3600)).to_i
$stdout.write "DRC finished at : #{time.hour}:#{time.min}:#{time.sec} - DRC duration = #{hours} hrs. #{minutes} min. #{seconds} sec.\n"
</text>
</klayout-macro>

View File

@ -0,0 +1,270 @@
<?xml version="1.0" encoding="utf-8"?>
<klayout-macro>
<description/>
<version/>
<category>lvs</category>
<prolog/>
<epilog/>
<doc/>
<autorun>false</autorun>
<autorun-early>false</autorun-early>
<shortcut/>
<show-in-menu>true</show-in-menu>
<group-name>lvs_scripts</group-name>
<menu-path>tools_menu.lvs.end</menu-path>
<interpreter>dsl</interpreter>
<dsl-interpreter-name>lvs-dsl-xml</dsl-interpreter-name>
<text>#
# Extraction for freePDK45
#
############################
tstart = Time.now
# optionnal for a batch launch : klayout -b -rd input=my_layout.gds -rd report=my_report.lyrdb -rd schematic=reference_netlist.cir -rd target_netlist=extracted_netlist.cir -r lvs_freepdk45.lvs
if $input
source($input)
end
if $report
report_lvs($report)
else
report_lvs("lvs_report.lvsdb")
end
if $schematic
#reference netlist
schematic($schematic)
else
# schematic("sram_8_256_freepdk45.sp")
schematic(RBA::CellView::active.filename.sub(/\.(oas|gds|oas.gz|gds.gz)$/, ".sp"))
end
# true: use net names instead of numbers
# false: use numbers for nets
spice_with_net_names = true
# true: put in comments with details
# false: no comments
spice_with_comments = false
if $target_netlist
target_netlist($target_netlist)
else
# target_netlist("netlist.cir", write_spice(spice_with_net_names, spice_with_comments), "The netlist comment goes here.")
target_netlist(File.join(File.dirname(RBA::CellView::active.filename), source.cell_name+"_extracted.cir"), write_spice(spice_with_net_names, spice_with_comments), "Extracted by KLayout on : #{Time.now.strftime("%d/%m/%Y %H:%M")}")
end
# Hierarchical mode
deep
# Use 4 CPU cores
threads(4)
# Print details
verbose(true)
# layers definitions
########################
active = input(1, 0)
pwell = input(2, 0)
nwell = input(3, 0)
nplus = input(4, 0)
pplus = input(5, 0)
vtg = input(6, 0)
vth = input(7, 0)
thkox = input(8, 0)
poly = input(9, 0)
cont = input(10, 0)
metal1 = input(11, 0)
metal1_lbl = input(11, 1)
metal1_pin = input(11, 2)
via1 = input(12, 0)
metal2 = input(13, 0)
metal2_lbl = input(13, 1)
metal2_pin = input(13, 2)
via2 = input(14, 0)
metal3 = input(15, 0)
metal3_lbl = input(15, 1)
metal3_pin = input(15, 2)
via3 = input(16, 0)
metal4 = input(17, 0)
metal4_lbl = input(17, 1)
metal4_pin = input(17, 2)
via4 = input(18, 0)
metal5 = input(19, 0)
metal5_lbl = input(19, 1)
metal5_pin = input(19, 2)
via5 = input(20, 0)
metal6 = input(21, 0)
metal6_lbl = input(21, 1)
metal6_pin = input(21, 2)
via6 = input(22, 0)
metal7 = input(23, 0)
metal7_lbl = input(23, 1)
metal7_pin = input(23, 2)
via7 = input(24, 0)
metal8 = input(25, 0)
metal8_lbl = input(25, 1)
metal8_pin = input(25, 2)
via8 = input(26, 0)
metal9 = input(27, 0)
metal9_lbl = input(27, 1)
metal9_pin = input(27, 2)
via9 = input(28, 0)
metal10 = input(29, 0)
metal10_lbl = input(29, 1)
metal10_pin = input(29, 2)
# Bulk layer for terminal provisioning
bulk = polygon_layer
# Computed layers
active_in_nwell = active &amp; nwell
pactive = active_in_nwell &amp; pplus
ntie = active_in_nwell &amp; nplus
pgate = pactive &amp; poly
psd = pactive - pgate
lv_pgate = pgate - vtg - thkox
gv_pgate = pgate &amp; vtg - vth - thkox
hv_pgate = pgate - vtg - vth &amp; thkox
active_in_pwell = active &amp; pwell
nactive = active_in_pwell &amp; nplus
ptie = active_in_pwell &amp; pplus
ngate = nactive &amp; poly
nsd = nactive - ngate
lv_ngate = ngate - vtg - thkox
gv_ngate = ngate &amp; vtg - vth - thkox
hv_ngate = ngate - vtg - vth &amp; thkox
cheat("cell_6t", "dummy_cell_6t", "cell_1rw", "dummy_cell_1rw", "cell_2rw", "dummy_cell_2rw", "dff","wordline_driver_0") {
# PMOS transistor device extraction
extract_devices(mos4("PMOS_VTL"), { "SD" =&gt; psd, "G" =&gt; lv_pgate, "tS" =&gt; psd, "tD" =&gt; psd, "tG" =&gt; poly, "W" =&gt; nwell })
extract_devices(mos4("PMOS_VTG"), { "SD" =&gt; psd, "G" =&gt; gv_pgate, "tS" =&gt; psd, "tD" =&gt; psd, "tG" =&gt; poly, "W" =&gt; nwell })
extract_devices(mos4("PMOS_VTH"), { "SD" =&gt; psd, "G" =&gt; hv_pgate, "tS" =&gt; psd, "tD" =&gt; psd, "tG" =&gt; poly, "W" =&gt; nwell })
# NMOS transistor device extraction
extract_devices(mos4("NMOS_VTL"), { "SD" =&gt; nsd, "G" =&gt; lv_ngate, "tS" =&gt; nsd, "tD" =&gt; nsd, "tG" =&gt; poly, "W" =&gt; pwell })
extract_devices(mos4("NMOS_VTG"), { "SD" =&gt; nsd, "G" =&gt; gv_ngate, "tS" =&gt; nsd, "tD" =&gt; nsd, "tG" =&gt; poly, "W" =&gt; pwell })
extract_devices(mos4("NMOS_VTH"), { "SD" =&gt; nsd, "G" =&gt; hv_ngate, "tS" =&gt; nsd, "tD" =&gt; nsd, "tG" =&gt; poly, "W" =&gt; pwell })
}
# Define connectivity for netlist extraction
# Inter-layer
connect(nwell, ntie)
connect(pwell, ptie)
connect(cont, ntie)
connect(cont, ptie)
connect(psd, cont)
connect(nsd, cont)
connect(poly, cont)
connect(cont, metal1)
connect(cont, metal1)
connect(metal1, via1)
connect(via1, metal2)
connect(metal2, via2)
connect(via2, metal3)
connect(metal3, via3)
connect(via3, metal4)
connect(metal4, via4)
connect(via4, metal5)
connect(metal5, via5)
connect(via5, metal6)
connect(metal6, via6)
connect(via6, metal7)
connect(metal7, via7)
connect(via7, metal8)
connect(metal8, via8)
connect(via8, metal9)
connect(metal9, via9)
connect(via9, metal10)
# attach labels :
connect(metal1, metal1_lbl)
connect(metal1, metal1_pin)
connect(metal2, metal2_lbl)
connect(metal2, metal2_pin)
connect(metal3, metal3_lbl)
connect(metal3, metal3_pin)
connect(metal4, metal4_lbl)
connect(metal4, metal4_pin)
connect(metal5, metal5_lbl)
connect(metal5, metal5_pin)
connect(metal6, metal6_lbl)
connect(metal6, metal6_pin)
connect(metal7, metal7_lbl)
connect(metal7, metal7_pin)
connect(metal8, metal8_lbl)
connect(metal8, metal8_pin)
connect(metal9, metal9_lbl)
connect(metal9, metal9_pin)
connect(metal10, metal10_lbl)
connect(metal10, metal10_pin)
# Global
schematic.simplify
connect_global(pwell, "PWELL")
connect_global(nwell, "NWELL")
connect_global(bulk, "BULK")
#for pat in %w(pnand*_0 and2_dec_0 port_address* replica_bitcell_array)
# connect_explicit(pat, [ "NWELL", "vdd" ])
# connect_explicit(pat, [ "BULK", "PWELL", "gnd" ])
#end
#for pat in %w(XOR* XNOR* TLAT* TINV* TBUF* SDFF* OR* OAI* NOR* NAND* MUX* LOGIC* INV* HA* FILLCELL*
# FA* DLL* DLH* DFF* DFFS* DFFR* DFFRS* CLKGATE* CLKBUF* BUF* AOI* ANTENNA* AND*)
# connect_explicit(pat, [ "NWELL", "VDD" ])
# connect_explicit(pat, [ "BULK", "VSS" ])
#end
# Actually performs the extraction
netlist # ... not really required
# Flatten cells which are present in one netlist only
align
# SIMPLIFICATION of the netlist
#netlist.make_top_level_pins
#netlist.combine_devices
#netlist.purge
#netlist.purge_nets
netlist.simplify
# Tolerances for the devices extracted parameters
# tolerance(device_class_name, parameter_name [, :absolute =&gt; absolute_tolerance] [, :relative =&gt; relative_tolerance])
tolerance("PMOS_LVT", "W", :absolute =&gt; 1.nm, :relative =&gt; 0.001)
tolerance("PMOS_LVT", "L", :absolute =&gt; 1.nm, :relative =&gt; 0.001)
tolerance("PMOS_GVT", "W", :absolute =&gt; 1.nm, :relative =&gt; 0.001)
tolerance("PMOS_GVT", "L", :absolute =&gt; 1.nm, :relative =&gt; 0.001)
tolerance("PMOS_HVT", "W", :absolute =&gt; 1.nm, :relative =&gt; 0.001)
tolerance("PMOS_HVT", "L", :absolute =&gt; 1.nm, :relative =&gt; 0.001)
tolerance("NMOS_LVT", "W", :absolute =&gt; 1.nm, :relative =&gt; 0.001)
tolerance("NMOS_LVT", "L", :absolute =&gt; 1.nm, :relative =&gt; 0.001)
tolerance("NMOS_GVT", "W", :absolute =&gt; 1.nm, :relative =&gt; 0.001)
tolerance("NMOS_GVT", "L", :absolute =&gt; 1.nm, :relative =&gt; 0.001)
tolerance("NMOS_HVT", "W", :absolute =&gt; 1.nm, :relative =&gt; 0.001)
tolerance("NMOS_HVT", "L", :absolute =&gt; 1.nm, :relative =&gt; 0.001)
#max_res(1000000)
#min_caps(1e-15)
max_branch_complexity(65536)
max_depth(16)
if ! compare
#raise "ERROR : Netlists don't match"
puts "ERROR : Netlists don't match"
else
puts "CONGRATULATIONS! Netlists match."
end
# time spent for the LVS
time = Time.now
hours = ((time - tstart)/3600).to_i
minutes = ((time - tstart)/60 - hours * 60).to_i
seconds = ((time - tstart) - (minutes * 60 + hours * 3600)).to_i
$stdout.write "LVS finished at : #{time.hour}:#{time.min}:#{time.sec} - LVS duration = #{hours} hrs. #{minutes} min. #{seconds} sec.\n"</text>
</klayout-macro>

View File

@ -460,8 +460,8 @@ parameter["bitcell_drain_cap"] = 0.1 # In Femto-Farad, approximation of d
# Technology Tool Preferences
###################################################
drc_name = "calibre"
lvs_name = "calibre"
pex_name = "calibre"
drc_name = "klayout"
lvs_name = "klayout"
pex_name = "klayout"
blackbox_bitcell = False

View File

@ -1,7 +0,0 @@
import os
CWD = os.environ.get("OPENRAM_TECH") + "/freepdk45/tf"
ui().importCds("default", CWD+"/display.drf", CWD+"/FreePDK45.tf", 1000, 1, CWD+"/layers.map")