blob: 5c33f12c21986e50ed6405b4290beefb803f370b [file] [log] [blame]
#=============================================================================================================================================================
#------------------------------------------------------------ GF 0.18um MCU DRC RULE DECK (NWELL) ------------------------------------------------------------
#=============================================================================================================================================================
require 'time'
require "logger"
exec_start_time = Time.now
logger = Logger.new(STDOUT)
logger.formatter = proc do |severity, datetime, progname, msg|
"#{datetime}: Memory Usage (" + `pmap #{Process.pid} | tail -1`[10,40].strip + ") : #{msg}
"
end
#================================================
#----------------- FILE SETUP -------------------
#================================================
# optional for a batch launch : klayout -b -r gf180mcu.drc -rd input=design.gds -rd report=gf180mcu_main.lyrdb
logger.info("Starting running GF180MCU Klayout DRC runset on %s" % [$input])
if $input
if $topcell
source($input, $topcell)
else
source($input)
end
end
logger.info("Loading database to memory is complete.")
if $report
logger.info("GF180MCU Klayout DRC runset output at: %s" % [$report])
report("DRC Run Report at", $report)
else
logger.info("GF180MCU Klayout DRC runset output at default location." % [File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb").path])
report("DRC Run Report at", File.join(File.dirname(RBA::CellView::active.filename), "gf180_drc.lyrdb"))
end
if $thr
logger.info("Number of threads to use %s" % [$thr])
threads($thr)
else
logger.info("Number of threads to use 16")
threads(16)
end
# === TILING MODE ===
if $run_mode == "tiling"
# use a tile size of 1mm - not used in deep mode-
# tiles(500.um)
# use a tile border of 10 micron:
# tile_borders(10.um)
tiles(1000)
logger.info("Tiling mode is enabled.")
elsif $run_mode == "deep"
#=== HIER MODE ===
deep
logger.info("deep mode is enabled.")
elsif $run_mode == "flat"
#=== FLAT MODE ===
flat
logger.info("flat mode is enabled.")
else
#=== FLAT MODE ===
flat
logger.info("flat mode is enabled.")
end # run_mode
#================================================
#------------- LAYERS DEFINITIONS ---------------
#================================================
v5_xtor = polygons(112, 1 )
dualgate = polygons(55 , 0 )
poly2 = polygons(30 , 0 )
nplus = polygons(32 , 0 )
pplus = polygons(31 , 0 )
comp = polygons(22 , 0 )
dnwell = polygons(12 , 0 )
nwell = polygons(21 , 0 )
lvpwell = polygons(204, 0 )
sab = polygons(49 , 0 )
res_mk = polygons(110, 5 )
ymtp_mk = polygons(86 , 17)
logger.info("Starting deriving base layers.")
#================================================
#------------- LAYERS DERIVATIONS ---------------
#================================================
ncomp = comp & nplus
pcomp = comp & pplus
tgate = poly2 & comp
ngate = nplus & tgate
pgate = pplus & tgate
#================================================
#------------------ SWITCHES --------------------
#================================================
logger.info("Evaluate switches.")
# FEOL
if $feol == "false"
FEOL = $feol
logger.info("FEOL is disabled.")
else
FEOL = "true"
logger.info("FEOL is enabled.")
end # FEOL
# BEOL
if $beol == "false"
BEOL = $beol
logger.info("BEOL is disabled.")
else
BEOL = "true"
logger.info("BEOL is enabled.")
end # BEOL
# connectivity rules
if $conn_drc == "true"
CONNECTIVITY_RULES = $conn_drc
logger.info("connectivity rules are enabled.")
else
CONNECTIVITY_RULES = false
logger.info("connectivity rules are disabled.")
end # connectivity rules
# METAL_TOP
if $metal_top
METAL_TOP = $metal_top
else
METAL_TOP = "9K"
end # METAL_TOP
logger.info("METAL_TOP Selected is %s" % [METAL_TOP])
# METAL_LEVEL
if $metal_level
METAL_LEVEL = $metal_level
else
METAL_LEVEL = "6LM"
end # METAL_LEVEL
logger.info("METAL_STACK Selected is %s" % [METAL_LEVEL])
# WEDGE
if $wedge == "false"
WEDGE = $wedge
else
WEDGE = "true"
end # WEDGE
logger.info("Wedge enabled %s" % [WEDGE])
# BALL
if $ball == "false"
BALL = $ball
else
BALL = "true"
end # BALL
logger.info("Ball enabled %s" % [BALL])
# GOLD
if $gold == "false"
GOLD = $gold
else
GOLD = "true"
end # GOLD
logger.info("Gold enabled %s" % [GOLD])
if $mim_option
MIM_OPTION = $mim_option
else
MIM_OPTION = "Nan"
end
logger.info("MIM Option selected %s" % [MIM_OPTION])
# OFFGRID
if $offgrid == "false"
OFFGRID = false
else
OFFGRID = true
end # OFFGRID
logger.info("Offgrid enabled %s" % [OFFGRID])
#================================================
#------------- LAYERS CONNECTIONS ---------------
#================================================
if CONNECTIVITY_RULES
logger.info("Construct connectivity for the design.")
connect(dnwell, ncomp)
connect(ncomp, contact)
connect(pcomp, contact)
connect(lvpwell, ncomp)
connect(nwell, ncomp)
connect(natcompsd, contact)
connect(mvsd, ncomp)
connect(mvpsd, pcomp)
connect(contact, 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, metaltop)
end #CONNECTIVITY_RULES
#================================================
#------------ PRE-DEFINED FUNCTIONS -------------
#================================================
def conn_space(layer,conn_val,not_conn_val, mode)
if conn_val > not_conn_val
raise "ERROR : Wrong connectivity implementation"
end
connected_output = layer.space(conn_val.um, mode).polygons(0.001)
unconnected_errors_unfiltered = layer.space(not_conn_val.um, mode)
singularity_errors = layer.space(0.001.um)
# Filter out the errors arising from the same net
unconnected_errors = DRC::DRCLayer::new(self, RBA::EdgePairs::new)
unconnected_errors_unfiltered.data.each do |ep|
net1 = l2n_data.probe_net(layer.data, ep.first.p1)
net2 = l2n_data.probe_net(layer.data, ep.second.p1)
if !net1 || !net2
puts "Should not happen ..."
elsif net1.circuit != net2.circuit || net1.cluster_id != net2.cluster_id
# unconnected
unconnected_errors.data.insert(ep)
end
end
unconnected_output = unconnected_errors.polygons.or(singularity_errors.polygons(0.001))
return connected_output, unconnected_output
end
def conn_separation(layer1, layer2, conn_val,not_conn_val, mode)
if conn_val > not_conn_val
raise "ERROR : Wrong connectivity implementation"
end
connected_output = layer1.separation(layer2, conn_val.um, mode).polygons(0.001)
unconnected_errors_unfiltered = layer1.separation(layer2, not_conn_val.um, mode)
# Filter out the errors arising from the same net
unconnected_errors = DRC::DRCLayer::new(self, RBA::EdgePairs::new)
unconnected_errors_unfiltered.data.each do |ep|
net1 = l2n_data.probe_net(layer1.data, ep.first.p1)
net2 = l2n_data.probe_net(layer2.data, ep.second.p1)
if !net1 || !net2
puts "Should not happen ..."
elsif net1.circuit != net2.circuit || net1.cluster_id != net2.cluster_id
# unconnected
unconnected_errors.data.insert(ep)
end
end
unconnected_output = unconnected_errors.polygons(0.001)
return connected_output, unconnected_output
end
# === IMPLICIT EXTRACTION ===
if CONNECTIVITY_RULES
logger.info("Connectivity rules enabled, Netlist object will be generated.")
netlist
end #CONNECTIVITY_RULES
# === LAYOUT EXTENT ===
CHIP = extent.sized(0.0)
logger.info("Total area of the design is #{CHIP.area()} um^2.")
#================================================
#---------------------NWELL----------------------
#================================================
if FEOL
logger.info("FEOL section")
# Rule NW.1a_3.3V: Min. Nwell Width (This is only for litho purpose on the generated area). is 0.86µm
logger.info("Executing rule NW.1a_3.3V")
nw1a_l1 = nwell.width(0.86.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
nw1a_l1.output("NW.1a_3.3V", "NW.1a_3.3V : Min. Nwell Width (This is only for litho purpose on the generated area). : 0.86µm")
nw1a_l1.forget
# Rule NW.1a_5V: Min. Nwell Width (This is only for litho purpose on the generated area). is 0.86µm
logger.info("Executing rule NW.1a_5V")
nw1a_l1 = nwell.width(0.86.um, euclidian).polygons(0.001).overlapping(dualgate)
nw1a_l1.output("NW.1a_5V", "NW.1a_5V : Min. Nwell Width (This is only for litho purpose on the generated area). : 0.86µm")
nw1a_l1.forget
nw_1b = nwell.outside(dnwell).and(res_mk).not(comp).not(poly2)
# Rule NW.1b_3.3V: Min. Nwell Width as a resistor (Outside DNWELL only). is 2µm
logger.info("Executing rule NW.1b_3.3V")
nw1b_l1 = nw_1b.width(2.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
nw1b_l1.output("NW.1b_3.3V", "NW.1b_3.3V : Min. Nwell Width as a resistor (Outside DNWELL only). : 2µm")
nw1b_l1.forget
# Rule NW.1b_5V: Min. Nwell Width as a resistor (Outside DNWELL only). is 2µm
logger.info("Executing rule NW.1b_5V")
nw1b_l1 = nw_1b.width(2.um, euclidian).polygons(0.001).overlapping(dualgate)
nw1b_l1.output("NW.1b_5V", "NW.1b_5V : Min. Nwell Width as a resistor (Outside DNWELL only). : 2µm")
nw1b_l1.forget
if CONNECTIVITY_RULES
logger.info("CONNECTIVITY_RULES section")
connected_nwell_3p3v, unconnected_nwell_3p3v = conn_space(nwell, 0.6, 1.4, euclidian)
connected_nwell_5p0v, unconnected_nwell_5p0v = conn_space(nwell, 0.74, 1.7, euclidian)
# Rule NW.2a_3.3V: Min. Nwell Space (Outside DNWELL) [Equi-potential], Merge if the space is less than. is 0.6µm
logger.info("Executing rule NW.2a_3.3V")
nw2a_l1 = connected_nwell_3p3v.not_inside(ymtp_mk).not_interacting(v5_xtor).not_interacting(dualgate)
nw2a_l1.output("NW.2a_3.3V", "NW.2a_3.3V : Min. Nwell Space (Outside DNWELL) [Equi-potential], Merge if the space is less than. : 0.6µm")
nw2a_l1.forget
# Rule NW.2a_5V: Min. Nwell Space (Outside DNWELL) [Equi-potential], Merge if the space is less than. is 0.74µm
logger.info("Executing rule NW.2a_5V")
nw2a_l1 = connected_nwell_5p0v.not_inside(ymtp_mk).overlapping(dualgate)
nw2a_l1.output("NW.2a_5V", "NW.2a_5V : Min. Nwell Space (Outside DNWELL) [Equi-potential], Merge if the space is less than. : 0.74µm")
nw2a_l1.forget
# Rule NW.2b_3.3V: Min. Nwell Space (Outside DNWELL) [Different potential]. is 1.4µm
logger.info("Executing rule NW.2b_3.3V")
nw2b_l1 = unconnected_nwell_3p3v.not_interacting(v5_xtor).not_interacting(dualgate)
nw2b_l1.output("NW.2b_3.3V", "NW.2b_3.3V : Min. Nwell Space (Outside DNWELL) [Different potential]. : 1.4µm")
nw2b_l1.forget
# Rule NW.2b_5V: Min. Nwell Space (Outside DNWELL) [Different potential]. is 1.7µm
logger.info("Executing rule NW.2b_5V")
nw2b_l1 = unconnected_nwell_5p0v.overlapping(dualgate)
nw2b_l1.output("NW.2b_5V", "NW.2b_5V : Min. Nwell Space (Outside DNWELL) [Different potential]. : 1.7µm")
nw2b_l1.forget
else
logger.info("CONNECTIVITY_RULES disabled section")
# Rule NW.2b_3.3V_: Min. Nwell Space (Outside DNWELL) [Different potential]. is 1.4µm
logger.info("Executing rule NW.2b_3.3V_")
nw2b_l1 = nwell.isolated(1.4.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
nw2b_l1.output("NW.2b_3.3V_", "NW.2b_3.3V_ : Min. Nwell Space (Outside DNWELL) [Different potential]. : 1.4µm")
nw2b_l1.forget
# Rule NW.2b_5V_: Min. Nwell Space (Outside DNWELL) [Different potential]. is 1.7µm
logger.info("Executing rule NW.2b_5V_")
nw2b_l1 = nwell.isolated(1.7.um, euclidian).polygons(0.001).overlapping(dualgate)
nw2b_l1.output("NW.2b_5V_", "NW.2b_5V_ : Min. Nwell Space (Outside DNWELL) [Different potential]. : 1.7µm")
nw2b_l1.forget
end #CONNECTIVITY_RULES
# Rule NW.3_3.3V: Min. Nwell to DNWELL space. is 3.1µm
logger.info("Executing rule NW.3_3.3V")
nw3_l1 = nwell.separation(dnwell, 3.1.um, euclidian).polygons(0.001).not_interacting(v5_xtor).not_interacting(dualgate)
nw3_l1.output("NW.3_3.3V", "NW.3_3.3V : Min. Nwell to DNWELL space. : 3.1µm")
nw3_l1.forget
# Rule NW.3_5V: Min. Nwell to DNWELL space. is 3.1µm
logger.info("Executing rule NW.3_5V")
nw3_l1 = nwell.separation(dnwell, 3.1.um, euclidian).polygons(0.001).overlapping(dualgate)
nw3_l1.output("NW.3_5V", "NW.3_5V : Min. Nwell to DNWELL space. : 3.1µm")
nw3_l1.forget
# Rule NW.4_3.3V: Min. Nwell to LVPWELL space.
logger.info("Executing rule NW.4_3.3V")
nw4_l1 = nwell.not_outside(lvpwell).not_interacting(v5_xtor).not_interacting(dualgate)
nw4_l1.output("NW.4_3.3V", "NW.4_3.3V : Min. Nwell to LVPWELL space.")
nw4_l1.forget
# Rule NW.4_5V: Min. Nwell to LVPWELL space.
logger.info("Executing rule NW.4_5V")
nw4_l1 = nwell.not_outside(lvpwell).overlapping(dualgate)
nw4_l1.output("NW.4_5V", "NW.4_5V : Min. Nwell to LVPWELL space.")
nw4_l1.forget
# Rule NW.5_3.3V: Min. DNWELL enclose Nwell. is 0.5µm
logger.info("Executing rule NW.5_3.3V")
nw5_l1 = dnwell.enclosing(nwell, 0.5.um, euclidian).polygons(0.001)
nw5_l2 = nwell.not_outside(dnwell).not(dnwell)
nw5_l = nw5_l1.or(nw5_l2).not_interacting(v5_xtor).not_interacting(dualgate)
nw5_l.output("NW.5_3.3V", "NW.5_3.3V : Min. DNWELL enclose Nwell. : 0.5µm")
nw5_l1.forget
nw5_l2.forget
nw5_l.forget
# Rule NW.5_5V: Min. DNWELL enclose Nwell. is 0.5µm
logger.info("Executing rule NW.5_5V")
nw5_l1 = dnwell.enclosing(nwell, 0.5.um, euclidian).polygons(0.001)
nw5_l2 = nwell.not_outside(dnwell).not(dnwell)
nw5_l = nw5_l1.or(nw5_l2).overlapping(dualgate)
nw5_l.output("NW.5_5V", "NW.5_5V : Min. DNWELL enclose Nwell. : 0.5µm")
nw5_l1.forget
nw5_l2.forget
nw5_l.forget
# Rule NW.6: Nwell resistors can only exist outside DNWELL.
logger.info("Executing rule NW.6")
nw6_l1 = nwell.inside(res_mk).interacting(dnwell)
nw6_l1.output("NW.6", "NW.6 : Nwell resistors can only exist outside DNWELL.")
nw6_l1.forget
# rule NW.6_5V is not a DRC check
# rule NW.7_3.3V is not a DRC check
# rule NW.7_5V is not a DRC check
end #FEOL
exec_end_time = Time.now
run_time = exec_end_time - exec_start_time
logger.info("DRC Run time %f seconds" % [run_time])