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

More LVS updates and adding advanced testing for IPs in the LVS #110

Merged
merged 13 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions .github/workflows/lvs_regression.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ jobs:
- name: Installing Klayout
run: |
sudo apt update -qq -y
wget https://www.klayout.org/downloads/Ubuntu-22/klayout_0.28.14-1_amd64.deb
wget https://www.klayout.org/downloads/Ubuntu-22/klayout_0.29.0-1_amd64.deb
# The dpkg command will fail complaining about missing dependencies.
sudo dpkg -i ./klayout_0.28.14-1_amd64.deb || true
sudo dpkg -i ./klayout_0.29.0-1_amd64.deb || true
# The apt install command should install the missing dependencies
# needed by KLayout above and finish the install.
sudo apt install -f -y
Expand All @@ -73,3 +73,25 @@ jobs:
- name: Testing ${{ matrix.part }} for ${{ matrix.test }}
run: |
make test-"$(python -c 'print("${{ matrix.part }}".upper())')"-${{ matrix.test }}

lvs_regression_cells:
runs-on: ubuntu-latest
steps:
- name: Installing Klayout
run: |
sudo apt update -qq -y
wget https://www.klayout.org/downloads/Ubuntu-22/klayout_0.29.0-1_amd64.deb
# The dpkg command will fail complaining about missing dependencies.
sudo dpkg -i ./klayout_0.29.0-1_amd64.deb || true
# The apt install command should install the missing dependencies
# needed by KLayout above and finish the install.
sudo apt install -f -y
# Check that KLayout was successfully installed!
klayout -v

- uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Testing LVS for SG13G2 cells
run: |
make test-LVS-cells
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,4 @@ cython_debug/
unit_tests_*
lvs_run_*
*_extracted.cir
cells_tests_*
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ test-LVS-% : env
@. $(VENV_RUN_COMMAND); echo "Running Klayout-LVS regression for $* device"
@. $(VENV_RUN_COMMAND); cd $(KLAYOUT_LVS_TESTS) && make test-LVS-$*

#=================================
# -------- test-LVS-cells --------
#=================================

test-LVS-cells: env
@. $(VENV_RUN_COMMAND); echo "Running Klayout-LVS for SG13G2 cells"
@. $(VENV_RUN_COMMAND); cd $(KLAYOUT_LVS_TESTS) && make test-LVS-cells

#=================================
# -------- test-LVS-switch -------
#=================================
Expand Down
73 changes: 11 additions & 62 deletions ihp-sg13g2/libs.tech/klayout/tech/lvs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,23 @@ Explains how to use the SG13G2 LVS rule decks.

```text
📁 lvs
┣ 📁testing Testing environment directory for SG13G2 LVS.
┣ 📁rule_decks Holds all LVS rule decks used for SG13G2.
┣ sg13g2.lvs Main LVS runset that call all rule decks.
┣ 📜README.md This file to document LVS for SG13G2.
┗ 📜run_lvs.py Main python script used for SG13G2 LVS.
┣ 📁testing Directory for the SG13G2 LVS testing environment.
┣ 📁rule_decks Contains all LVS rule decks used for SG13G2.
📜sg13g2.lvs Main LVS runset that calls all rule decks.
┣ 📜README.md Documentation for SG13G2 LVS.
┗ 📜run_lvs.py Main Python script for SG13G2 LVS run.
```

## Prerequisites

You need the following set of tools installed to be able to run SG13G2 LVS:

At a minimum:

- Python 3.9+
- KLayout 0.28.14+
- KLayout 0.29.0+

We have tested this using the following setup:
- Python 3.9.18
- KLayout 0.29.0

## Installation

Expand All @@ -43,59 +45,6 @@ To install the required Python packages, execute the following command:
pip install -r ../../../../../requirements.txt
```

## Devices Status

The following table explains the list of available SG13G2 devices we have supported in our LVS runset.

| Device | Tested |
|-----------------|------------------|
| **MOSFET** | |
| sg13_lv_nmos |:white_check_mark:|
| sg13_hv_nmos |:white_check_mark:|
| sg13_lv_pmos |:white_check_mark:|
| sg13_hv_pmos |:white_check_mark:|
| **RF-MOSFET** | |
| rfnmos |:white_check_mark:|
| rfnmosHV |:white_check_mark:|
| rfpmos |:white_check_mark:|
| rfpmosHV |:white_check_mark:|
| **BJTs** | |
| npn13G2 |:white_check_mark:|
| npn13G2L |:white_check_mark:|
| npn13G2V |:white_check_mark:|
| pnpMPA |:white_check_mark:|
| **Diodes** | |
| dantenna |:white_check_mark:|
| dpantenna |:white_check_mark:|
| schottky_nbl1 |:white_check_mark:|
| **Resistors** | |
| rsil |:white_check_mark:|
| rppd |:white_check_mark:|
| rhigh |:white_check_mark:|
| lvsres |:white_check_mark:|
| **Capacitors** | |
| SVaricap |:white_check_mark:|
| cap_cmim |:white_check_mark:|
| rfcmim |:white_check_mark:|
| **ESD** | |
| diodevdd_4kv |:white_check_mark:|
| diodevdd_2kv |:white_check_mark:|
| diodevss_4kv |:white_check_mark:|
| diodevss_2kv |:white_check_mark:|
| idiodevdd_4kv |:white_check_mark:|
| idiodevdd_2kv |:white_check_mark:|
| idiodevss_4kv |:white_check_mark:|
| idiodevss_2kv |:white_check_mark:|
| nmoscl_2 |:white_check_mark:|
| nmoscl_4 |:white_check_mark:|
| **Inductors** | |
| inductor |:white_check_mark:|
| inductor3 |:white_check_mark:|
| **Taps** | |
| ptap1 |:white_check_mark:|
| ntap1 |:white_check_mark:|


## Usage

You have the option to execute the SG13G2-LVS through either a Python script via the command-line interface [CLI](#cli) or by the Klayout graphical user interface [GUI](#gui), as detailed in the subsequent usage sections.
Expand All @@ -115,7 +64,7 @@ The `run_lvs.py` script takes your gds and netlist files to run LVS rule decks w

**Options:**

- `--help -h ` Displays this help message.
- `--help -h` Displays this help message.

- `--layout=<layout_path>` Specifies the file path of the input GDS file.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,11 @@ class DeviceCustomInd < RBA::DeviceClassInductor
add_terminals(name, num, 1)

# 5% tolerance for w,s,d:
equal_device_parameters = RBA::EqualDeviceParameters::new(parameter_id('w'), 0.0, 0.05)
equal_device_parameters += RBA::EqualDeviceParameters::new(parameter_id('s'), 0.0, 0.05)
equal_device_parameters += RBA::EqualDeviceParameters::new(parameter_id('d'), 0.0, 0.05)
equal_ind_parameters = RBA::EqualDeviceParameters::new(parameter_id('w'), 0.0, 0.05)
equal_ind_parameters += RBA::EqualDeviceParameters::new(parameter_id('s'), 0.0, 0.05)
equal_ind_parameters += RBA::EqualDeviceParameters::new(parameter_id('d'), 0.0, 0.05)
# applies the compare delegate:
self.equal_parameters = equal_device_parameters
self.equal_parameters = equal_ind_parameters

self.combiner = nil
self.supports_serial_combination=false
Expand Down Expand Up @@ -147,20 +147,17 @@ class RES2 < RBA::DeviceClassResistor
end
end

# MIMCAP device class
class MIMCap < RBA::DeviceClassCapacitor
def initialize
super
enable_parameter('C', false)
enable_parameter('A', true)
enable_parameter('P', true)
end
end

# Diode device class
class EnDiode < RBA::DeviceClassDiode
def initialize
super

# 1% tolerance for A,P:
equal_diode_parameters = RBA::EqualDeviceParameters::new(parameter_id('A'), 0.0, 0.01)
equal_diode_parameters += RBA::EqualDeviceParameters::new(parameter_id('P'), 0.0, 0.01)
# applies the compare delegate:
self.equal_parameters = equal_diode_parameters

# combiner
self.combiner = DiodeDeviceCombiner.new
self.supports_serial_combination=true
self.supports_parallel_combination=true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ class DeviceCustomMIM < RBA::DeviceClassCapacitor
clear_equivalent_terminal_ids

# Adding params
add_parameter(RBA::DeviceParameterDefinition.new('w', 'width', 0, true, 1e-6))
add_parameter(RBA::DeviceParameterDefinition.new('l', 'length', 0, true, 1e-6))
add_parameter(RBA::DeviceParameterDefinition.new('m', 'multiplier', 1, true, 1.0))
add_parameter(RBA::DeviceParameterDefinition.new('w', 'width', 0, false))
add_parameter(RBA::DeviceParameterDefinition.new('l', 'length', 0, false))
add_parameter(RBA::DeviceParameterDefinition.new('A', 'area', 0, true))
add_parameter(RBA::DeviceParameterDefinition.new('P', 'perimeter', 0, true))
add_parameter(RBA::DeviceParameterDefinition.new('m', 'multiplier', 1, true))

# Adding terminals
ter1 = add_terminal(RBA::DeviceTerminalDefinition.new("mim_top"))
Expand All @@ -46,7 +48,7 @@ class DeviceCustomMIM < RBA::DeviceClassCapacitor
# Adding extra param & terminal for rfcmim
return unless name.downcase.include?('rfcmim')

add_parameter(RBA::DeviceParameterDefinition.new('wfeed', 'feed width', 0, true, 1e-6))
add_parameter(RBA::DeviceParameterDefinition.new('wfeed', 'feed width', 0, true))
sub_ter = add_terminal(RBA::DeviceTerminalDefinition.new("mim_sub"))
sub_ter.name = "mim_sub"
end
Expand Down Expand Up @@ -170,6 +172,8 @@ class MIMCAPExtractor < RBA::GenericDeviceExtractor
device.set_parameter('w', width * $unit)
device.set_parameter('l', length * $unit)
end
device.set_parameter('A', width * length * $unit * $unit)
device.set_parameter('P', (width + length) * 2 * $unit)

end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,37 @@

# Custom reader for subcircuit models
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/)

case element
when 'R'
# For 2 term res [<nets> <model> <params>]
num_terms = 3
when 'C'
# Determine number of terms based on component type
num_terms =
if line.downcase.include?('varicap')
5
elsif line.downcase.include?('rfcmim')
4
else
3
end
end
line_no_param = line.split(' ').take(num_terms).join(' ')
"#{line_no_param.strip} #{valid_params.join(' ')}"
end

# Override parse_element method to handle exceptions gracefully
def parse_element(line, element)
# Prep sch for R, C
line = clean_sch(line, element) if %w[R C].include?(element)

super
rescue StandardError
case element
Expand Down Expand Up @@ -265,20 +294,23 @@ class CustomReader < RBA::NetlistSpiceReaderDelegate

# Map parameters for a diode device.
def map_diode_params(device, model, params)
if model.downcase.include?('diodev') || model.downcase.include?('schottky') || model.downcase.include?('nmoscl')
device.set_parameter('m', params['M'] || 1.0)
else
unless model.downcase.include?('diodev') || model.downcase.include?('schottky') || model.downcase.include?('nmoscl')
device.set_parameter('A', (params['A'] || ((params['W'] || 0.0) * (params['L'] || 0.0))) * 1e12)
device.set_parameter('P', (params['P'] || (((params['W'] || 0.0) + (params['L'] || 0.0)) * 2)) * 1e6)
device.set_parameter('m', params['M'] || 1.0)
end
device.set_parameter('m', params['M'] || 1.0)
end

# Map parameters for a capacitor device.
def map_capacitor_params(device, model, params)
device.set_parameter('w', (params['W'] || 0.0) * 1e6)
device.set_parameter('l', (params['L'] || 0.0) * 1e6)
device.set_parameter('m', params['M'] || params['MF'] || 1.0) if model.downcase.include?('cap_cmim')

if model.downcase.include?('mim')
device.set_parameter('A', (params['A'] || ((params['W'] || 0.0) * (params['L'] || 0.0))) * 1e12)
device.set_parameter('P', (params['P'] || (((params['W'] || 0.0) + (params['L'] || 0.0)) * 2)) * 1e6)
end
return unless model.downcase.include?('rfcmim')

device.set_parameter('wfeed', (params['WFEED'] || 0.0) * 1e6)
Expand All @@ -288,7 +320,8 @@ class CustomReader < RBA::NetlistSpiceReaderDelegate
def map_resistor_params(device, model, params)
if model.downcase.include?('tap')
device.set_parameter('A', (params['A'] || ((params['W'] || 0.0) * (params['L'] || 0.0))) * 1e12)
device.set_parameter('P', (params['P'] || params['PERIM'] || (((params['W'] || 0.0) + (params['L'] || 0.0)) * 2)) * 1e6)
device.set_parameter('P',
(params['P'] || params['PERIM'] || (((params['W'] || 0.0) + (params['L'] || 0.0)) * 2)) * 1e6)
elsif RES_DEV.any? { |res| model.downcase.start_with?(res) }
device.set_parameter('w', (params['W'] || 0.0) * 1e6)
device.set_parameter('l', (params['L'] || 0.0) * 1e6)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ diode_exclude = gatpoly_drw.join(nsd_drw)
.join(activ_mask).join(recog_esd).join(ind_drw)
.join(ind_pin).join(substrate_drw)

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

antenna_d_mk = recog_diode.not(antenna_d_exc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ connect(topvia2_drw, topmetal2_con)
# Attaching labels
connect(activ_drw, activ_label)
connect(poly_con, gatpoly_label)
connect(metal1_con, metal1_label)
connect(metal2_drw, metal2_label)
connect(metal3_drw, metal3_label)
connect(metal4_drw, metal4_label)
connect(metal5_drw, metal5_label)
connect(topmetal1_con, topmetal1_label)
connect(topmetal2_con, topmetal2_label)
connect(metal1_con, metal1_text)
connect(metal2_drw, metal2_text)
connect(metal3_drw, metal3_text)
connect(metal4_drw, metal4_text)
connect(metal5_drw, metal5_text)
connect(topmetal1_con, topmetal1_text)
connect(topmetal2_con, topmetal2_text)

logger.info('Starting SG13G2 LVS connectivity setup (Global)')
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ nwell_n_iso = nwell_drw.not(nbulay_drw)

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

# psd, nsd active & res
Expand Down Expand Up @@ -70,7 +70,7 @@ ntap = nactiv.and(nwell_n_iso).not(res_mk).not(recog_diode).not(gatpoly_drw)
ptap = pactiv.and(pwell).not(substrate_drw).not(res_mk).not(recog_diode).not(gatpoly_drw)

# Derived - Layers (Special)
nwell_holes = nwell_drw.holes
nwell_holes = nwell_drw.holes.not(nwell_drw)
ptap_holes = ptap.holes
ntap_holes = ntap.holes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ logger.info('Starting Inductor CONNECTIONS')
connect(ind2_ports, ind_pin)
connect(ind_pin, ind_text)
connect(ind_pin, topmetal2_con)
connect(ind2_sub, pwell_block)
connect(pwell_block, pwell)
connect(ind2_sub, pwell)

# ind3
connect(ind3_ports, ind_pin)
connect(ind_pin, topmetal1_con)
connect(ind3_sub, pwell_block)
connect(ind3_sub, pwell)
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ ind_port_lc = ind_ports_.interacting(ind_text.texts("LC"))
ind2_ports = ind_port_la.join(ind_port_lb)
ind2_core = ind_core_.interacting(ind_port_la, 1).interacting(ind_port_lb, 1)
ind2_mk = ind_drw.interacting(ind2_core).interacting(ind2_ports).not_interacting(ind_port_lc)
ind2_sub = pwell_block.and(ind_drw).interacting(ind2_core)
ind2_sub = pwell_block.and(ind_drw).interacting(ind2_core).sized(1.nm)

# inductor3
ind3_ports = ind_port_la.join(ind_port_lb).join(ind_port_lc)
ind3_core = ind_core_.interacting(ind_port_la, 1).interacting(ind_port_lb, 1).interacting(ind_port_lc, 1)
ind3_mk = ind_drw.interacting(ind3_core).interacting(ind3_ports)
ind3_sub = pwell_block.and(ind_drw).interacting(ind3_core)
ind3_sub = pwell_block.and(ind_drw).interacting(ind3_core).sized(1.nm)
Loading
Loading