Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LVS updates #124

Merged
merged 8 commits into from
May 28, 2024
6 changes: 4 additions & 2 deletions .github/workflows/linting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
# SPDX-License-Identifier: Apache-2.0
#==========================================================================

name: code linting
name: Python Code Linting

on:
pull_request:
# push:
paths:
- 'ihp-sg13g2/libs.tech/klayout/tech/lvs/**'
workflow_dispatch:

jobs:
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/lvs_regression.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# SPDX-License-Identifier: Apache-2.0
#==========================================================================

name: LVS regression testing
name: LVS Regression Testing

# Prevent keeping resources busy when a branch/PR is updated
# https://docs.github.com/en/actions/using-jobs/using-concurrency
Expand All @@ -24,8 +24,9 @@ concurrency:
cancel-in-progress: true

on:
# push:
pull_request:
paths:
- 'ihp-sg13g2/libs.tech/klayout/tech/lvs/**'
workflow_dispatch:

jobs:
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ env: $(TOP_DIR)/actions_venv

# Lint python code
lint_python: env
@echo "Running python linting for Klayout-tech directory"
@. $(VENV_RUN_COMMAND); flake8 ihp-sg13g2/libs.tech/klayout/tech/
@echo "Running python linting for Klayout-LVS scripts"
@. $(VENV_RUN_COMMAND); flake8 ihp-sg13g2/libs.tech/klayout/tech/lvs

#=================================
# ----- test-LVS_regression ------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ logger.info('Starting BJT DERIVATIONS')

logger.info('Starting NPN-BJT DERIVATIONS')

bjt_exclude = gatpoly_drw.join(pwell_block).join(nsd_drw)
bjt_exclude = gatpoly.join(pwell_block).join(nsd_drw)
.join(salblock_drw).join(polyres_drw).join(extblock_drw)
.join(res_drw).join(recog_diode).join(recog_esd)
.join(ind_drw).join(ind_pin).join(substrate_drw)
Expand All @@ -42,19 +42,19 @@ npn_c_exc = emwind_drw.join(emwihv_drw).join(activ_mask)
.join(nsd_block).join(npn_exclude)
npn_b_exc = emwind_drw.join(emwihv_drw).join(npn_exclude)
npn_sub = npn_mk.not(npn_exclude)
npn_dev = activ_drw.join(activ_mask).and(npn_mk)
npn_dev = activ.join(activ_mask).and(npn_mk)

# ---------- npn13G2 ----------
# npn13G2 exclusion layers
npn13G2_e_exc = activ_drw.join(emwihv_drw).join(npn_exclude)
npn13G2_e_exc = activ.join(emwihv_drw).join(npn_exclude)
npn13G2_b_exc = npn_b_exc.join(activ_mask)

# npn13G2 nodes
npn13G2_e_ = emwind_drw.and(activ_mask).and(nsd_block).and(npn_mk).not(npn13G2_e_exc)
# npn13G2 is a fixed device (0.07um X 0.9um)
npn13G2_e_pin = npn13G2_e_.with_bbox_min(0.07.um).with_bbox_max(0.9.um).with_area(0.063.um)
npn13G2_b_pin = nsd_block.and(npn_mk).not(npn13G2_b_exc)
npn13G2_c_pin = activ_drw.and(npn_mk).not_overlapping(npn_c_exc)
npn13G2_c_pin = activ.and(npn_mk).not_overlapping(npn_c_exc)

npn13G2_dev = npn_dev.join(nsd_block).extents.covering(npn13G2_e_pin).covering(npn13G2_b_pin).covering(npn13G2_c_pin)
npn13G2_c = npn13G2_dev.sized(-1.nm)
Expand All @@ -67,10 +67,10 @@ npn13G2_te = npn13G2_e
# ---------- npn13G2L ----------
# npn13G2L exclusion layers
npn13G2l_e_exc = activ_mask.join(nsd_block).join(emwihv_drw).join(npn_exclude)
npn13G2l_b_exc = npn_b_exc.join(activ_drw).join(nsd_block)
npn13G2l_b_exc = npn_b_exc.join(activ).join(nsd_block)

# npn13G2L nodes
npn13G2l_e_ = emwind_drw.and(activ_drw).and(npn_mk).not(npn13G2l_e_exc)
npn13G2l_e_ = emwind_drw.and(activ).and(npn_mk).not(npn13G2l_e_exc)
# npn13G2L has fixed width (0.07um), Length could vary from 1:2.5 um
npn13G2l_e_pin = npn13G2l_e_.with_bbox_min(0.07.um).with_bbox_max(1.um, 2.5.um).with_area(0.07.um, 0.175.um)
npn13G2l_b_pin = activ_mask.and(npn_mk).not(npn13G2l_b_exc)
Expand All @@ -89,7 +89,7 @@ npn13G2l_te = npn13G2l_e
npn13G2v_e_exc = activ_mask.join(nsd_block).join(emwind_drw).join(npn_exclude)

# npn13G2V nodes
npn13G2v_e_ = emwihv_drw.and(activ_drw).and(npn_mk).not(npn13G2v_e_exc)
npn13G2v_e_ = emwihv_drw.and(activ).and(npn_mk).not(npn13G2v_e_exc)
# npn13G2L has fixed width (0.12um), Length could vary from 1:5 um
npn13G2v_e_pin = npn13G2v_e_.with_bbox_min(0.12.um).with_bbox_max(1.um, 5.um).with_area(0.12.um, 0.6.um)
npn13G2v_b_pin = npn13G2l_b_pin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ varicap_exc = pwell.join(pwell_block).join(nwell_holes).join(cap_exc)
varicap_core = ngate_hv_base.and(nwell_iso).not(varicap_exc)
varicap_diff_port = nactiv.interacting(varicap_core).not(varicap_core)
.and(nwell_iso).not(varicap_exc).sized(-1.nm)
varicap_poly_port = gatpoly_drw.interacting(varicap_core)
varicap_poly_port = gatpoly.interacting(varicap_core)
varicap_ports = varicap_poly_port.join(varicap_diff_port)
varicap_sub = ptap.and(thickgateox_drw)
varicap_dev_mk = thickgateox_drw.covering(varicap_core).interacting(varicap_ports)
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ logger.info('Extracting sg13_hv_svaricap varactor')
extract_devices(GeneralNTerminalExtractor.new('sg13_hv_svaricap', 3), {
'core' => varicap_core,
'ports' => varicap_ports,
'meas_mk' => activ_drw,
'meas_mk' => activ,
'dev_mk' => varicap_dev_mk,
'sub_mk' => varicap_sub
})
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,24 @@ $unit = dbu
# --------------- CUSTOM CLASSES ----------------
#================================================

#=============== UTILS ===============
# Method to convert glob pattern to a case-insensitive glob-style pattern
def glob_to_case_insensitive_glob(glob)
wildcards = ['*', '?']

pattern = glob.chars.map do |c|
if c =~ /[A-Za-z]/
"[#{c.upcase}#{c.downcase}]"
elsif wildcards.include?(c)
c # Keep wildcard characters as they are
else
Regexp.escape(c)
end
end.join

pattern
end

#=============== CUSTOM READER ===================

# %include custom_reader.lvs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
class CustomReader < RBA::NetlistSpiceReaderDelegate
# Cleanup sch for R, C elements
def clean_sch(line, element)
line = line.delete('[]$\\/')

# Extracting parameters with values
valid_params = line.scan(/\b\w+\s*=\s*\S+\b/)

Expand All @@ -49,6 +47,9 @@ class CustomReader < RBA::NetlistSpiceReaderDelegate

# Override parse_element method to handle exceptions gracefully
def parse_element(line, element)
# clean line
line = line.delete('[]$\\/')

# Prep sch for R, C
line = clean_sch(line, element) if %w[R C].include?(element)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@

logger.info('Starting DIODE DERIVATIONS')

diode_exclude = gatpoly_drw.join(nsd_drw)
.join(trans_drw).join(emwind_drw).join(emwihv_drw)
.join(polyres_drw).join(extblock_drw).join(res_drw)
.join(activ_mask).join(recog_esd).join(ind_drw)
.join(ind_pin).join(substrate_drw)
diode_exclude = gatpoly.join(nsd_drw).join(trans_drw)
.join(emwind_drw).join(emwihv_drw).join(polyres_drw)
.join(extblock_drw).join(res_drw).join(activ_mask)
.join(recog_esd).join(ind_drw).join(ind_pin)
.join(substrate_drw)

antenna_d_exc = pwell_block.join(salblock_drw)
.join(nsd_block).join(diode_exclude)

antenna_d_mk = recog_diode.not(antenna_d_exc)

# ==== dantenna diode ====
dantenna_n = activ_drw.and(antenna_d_mk).not(psd_drw).not(nwell_drw)
dantenna_n = activ.and(antenna_d_mk).not(psd_drw).not(nwell_drw)
dantenna_p = pwell.and(antenna_d_mk).covering(dantenna_n)

# ==== dpantenna diode ====
Expand All @@ -48,17 +48,17 @@ schottky_mk = recog_diode.and(thickgateox_drw).not(diode_exclude)
.and(salblock_drw).and(nsd_block).and(nwell_holes)
.not(psd_drw).not(pwell).not(diode_exclude)

schottcky_p_ = cont_drw.and(activ_drw).and(metal1_con)
schottcky_p_ = cont_drw.and(activ).and(metal1_con)
.and(schottky_mk)

# schottky_nbl1 is a fixed device (0.3um X 1.0 um)
schottcky_p = schottcky_p_.with_bbox_min(0.3.um).with_bbox_max(1.0.um)
# Using box with area 1x1 to be used as a reference to (m)
schottcky_p_1x1 = schottcky_p.middle(as_boxes).sized(0.499.um)

schottcky_n = nsd_block.and(activ_drw).covering(schottcky_p)
schottcky_n = nsd_block.and(activ).covering(schottcky_p)

# define port for schottcky
schottcky_n_port = activ_drw.interacting(nwell_iso).interacting(schottcky_n).not(schottcky_n.sized(-1.nm))
schottcky_n_port = activ.interacting(nwell_iso).interacting(schottcky_n).not(schottcky_n.sized(-1.nm))
schottcky_n_con = cont_drw.and(schottcky_n_port).not_interacting(schottcky_p)
schottcky_sub = ptap.extents.covering(schottcky_p).covering(schottcky_n)
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ logger.info('Starting ESD DERIVATIONS')
# General
esd_exclude = nsd_block.join(nsd_drw).join(trans_drw)
.join(emwind_drw).join(emwihv_drw).join(polyres_drw)
.join(extblock_drw).join(res_drw).join(recog_diode)
.join(substrate_drw).join(ind_drw).join(ind_pin)
.join(extblock_drw).join(res_drw).join(substrate_drw)
.join(ind_drw).join(ind_pin)

esd_exc_d = gatpoly_drw.join(thickgateox_drw).join(salblock_drw)
esd_exc_d = gatpoly.join(thickgateox_drw).join(salblock_drw)
.join(esd_exclude)

idiodevdd_exc = esd_exc_d.join(nwell_holes)
Expand Down Expand Up @@ -125,17 +125,21 @@ idiodevss_4kv_tb = cont_drw.and(idiodevss_4kv_b).not_interacting(idiodevss_2kv_e
nmoscl_exc = esd_exclude.join(pwell_block)

# nmoscl_2
nmoscl_2_patt = glob_to_case_insensitive_glob("nmoscl_2")

gate_moscl = ngate_hv_base.and(salblock_drw).and(nbulay_drw)
nmoscl_2_n_ = recog_esd.interacting(text_drw.texts("nmoscl_2"))
nmoscl_2_n_ = recog_esd.interacting(text_drw.texts(nmoscl_2_patt))
nmoscl_2_n = nmoscl_2_n_.interacting(gate_moscl, 12)
nmoscl_2_n_port = nwell_drw.and(nmoscl_2_n)
nmoscl_2_p_ = ptap.and(nbulay_drw).inside(nmoscl_2_n_)
# Using box with area 1x1 to be used as a reference to (m)
nmoscl_2_p =nmoscl_2_p_.middle(as_boxes).sized(0.499.um)

# nmoscl_4
nmoscl_4_patt = glob_to_case_insensitive_glob("nmoscl_4")

gate_moscl = ngate_hv_base.and(salblock_drw).and(nbulay_drw)
nmoscl_4_n_ = recog_esd.interacting(text_drw.texts("nmoscl_4"))
nmoscl_4_n_ = recog_esd.interacting(text_drw.texts(nmoscl_4_patt))
nmoscl_4_n = nmoscl_4_n_.interacting(gate_moscl, 24)
nmoscl_4_n_port = nwell_drw.and(nmoscl_4_n)
nmoscl_4_p_ = ptap.and(nbulay_drw).inside(nmoscl_4_n_)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,43 +24,45 @@
logger.info('Starting general LVS derivations')

#=== Global Layers ===
CHIP = extent.sized(0.0)
# === CHIP ===
CHIP = case $run_mode
when 'deep'
extent('*')
else
#=== FLAT MODE ===
extent.sized(0.0)
end

# === General Derivations ===
# nwell
nwell_iso = nwell_drw.and(nbulay_drw)
nwell_holes = nwell_drw.holes.not(nwell_drw)

# pwell
pwell_pre = CHIP.not(nwell_drw).not(pwell_block).not(digisub_drw)
digisub_pre = digisub_drw.sized(-1.nm).not(nwell_drw).not(pwell_block)
pwell = pwell_pre.join(digisub_pre)
pwell_allowed = CHIP.not(pwell_block)
digisub_gap = digisub_drw.not(digisub_drw.sized(-1.nm))
pwell = pwell_allowed.not(nwell_drw).not(digisub_gap)

# General pwell
pwell_sub = CHIP.not(digisub_drw).not(pwell_block).not(nbulay_drw.interacting(nwell_holes))

# psd, nsd active & res
psd = psd_drw
nsd_res = nsd_drw.and(psd).interacting(polyres_drw)
nsd_all = CHIP.not(psd.join(nsd_block)).join(nsd_res)
pwell_sub = pwell_allowed.not(digisub_drw).not(nbulay_drw.interacting(nwell_holes))

# n & p activ
nactiv = activ_drw.not(psd.join(nsd_block))
pactiv = activ_drw.and(psd)
nactiv = activ.not(psd_drw.join(nsd_block))
pactiv = activ.and(psd_drw)

# res/cap exclusion
res_mk = polyres_drw.join(res_drw)
poly_con = gatpoly_drw.not(res_mk)
metal1_con = metal1_drw.not(metal1_res)
metal2_con = metal2_drw.not(metal2_res)
metal3_con = metal3_drw.not(metal3_res)
metal4_con = metal4_drw.not(metal4_res)
metal5_con = metal5_drw.not(metal5_res)
topmetal1_con = topmetal1_drw.not(topmetal1_res).not(ind_drw)
topmetal2_con = topmetal2_drw.not(topmetal2_res).not(ind_drw)
poly_con = gatpoly.not(res_mk)
metal1_con = metal1.not(metal1_res)
metal2_con = metal2.not(metal2_res)
metal3_con = metal3.not(metal3_res)
metal4_con = metal4.not(metal4_res)
metal5_con = metal5.not(metal5_res)
topmetal1_con = topmetal1.not(topmetal1_res).not(ind_drw)
topmetal2_con = topmetal2.not(topmetal2_res).not(ind_drw)

# Gate FETs
tgate = gatpoly_drw.and(activ_drw).not(res_mk)
tgate = gatpoly.and(activ).not(res_mk)
ngate = nactiv.and(tgate)
pgate = pactiv.and(tgate)
ngate_lv_base = ngate.not(thickgateox_drw)
Expand All @@ -74,15 +76,18 @@ psd_fet = pactiv.and(nwell_drw).interacting(pgate).not(pgate).not_interacting(re


# n1/p1 taps labels
ntap1_lbl = text_drw.texts("well")
well_patt = glob_to_case_insensitive_glob("well")
sub_patt = glob_to_case_insensitive_glob("sub!")

ntap1_lbl = text_drw.texts(well_patt)
ntap1_mk = nwell_drw.interacting(ntap1_lbl)

ptap1_lbl = text_drw.texts("sub!")
ptap1_lbl = text_drw.texts(sub_patt)
ptap1_mk = substrate_drw.and(pwell).interacting(ptap1_lbl)

# n & p taps (short connections)
ntap = nactiv.and(nwell_drw).not(recog_diode).not(gatpoly_drw).not(ntap1_mk)
ptap = pactiv.and(pwell).not(ptap1_mk).not(recog_diode).not(gatpoly_drw)
ntap = nactiv.and(nwell_drw).not(recog_diode).not(gatpoly).not(ntap1_mk)
ptap = pactiv.and(pwell).not(ptap1_mk).not(recog_diode).not(gatpoly)
ptap_holes = ptap.holes
ntap_holes = ntap.holes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ connect(ind2_ports, ind_pin)
connect(ind_pin, ind_text)
connect(ind_pin, topmetal2_con)
connect(ind2_sub, pwell)
connect(ind2_sub, nwell_drw)

# ind3
connect(ind3_ports, ind_pin)
connect(ind_pin, topmetal1_con)
connect(ind3_sub, pwell)
connect(ind3_sub, nwell_drw)
Loading